﻿///////////////////////////////////////////////////////////////////////////////////////////////////////
// Copyright (c) 2023, ООО 1С-Софт
// Все права защищены. Эта программа и сопроводительные материалы предоставляются 
// в соответствии с условиями лицензии Attribution 4.0 International (CC BY 4.0)
// Текст лицензии доступен по ссылке:
// https://creativecommons.org/licenses/by/4.0/legalcode
///////////////////////////////////////////////////////////////////////////////////////////////////////

#Область СлужебныйПрограммныйИнтерфейс

// См. ОбновлениеИнформационнойБазыБСП.ПриДобавленииОбработчиковОбновления.
Процедура ПриДобавленииОбработчиковОбновления(Обработчики) Экспорт
	
	Обработчик = Обработчики.Добавить();
	Обработчик.Версия          = "*";
	Обработчик.РежимВыполнения = "Отложенно";
	Обработчик.Идентификатор   = Новый УникальныйИдентификатор("c17ea385-6085-471f-ab94-219ec30a5a38");
	Обработчик.Комментарий     = НСтр("ru = 'Обновляются правила проверки учета в соответствие с изменениями в новой версии программы.
		|До завершения обработки часть проверок ведения учета будет недоступна.'");
	Обработчик.Процедура       = "КонтрольВеденияУчетаСлужебный.ОбновитьВспомогательныеДанныеПоИзменениямКонфигурации";
	
	Обработчик = Обработчики.Добавить();
	Обработчик.Версия                              = "3.0.1.25";
	Обработчик.Идентификатор                       = Новый УникальныйИдентификатор("4a240e04-87df-4c10-9f7f-97969c61e84f");
	Обработчик.Процедура                           = "РегистрыСведений.РезультатыПроверкиУчета.ОбработатьДанныеДляПереходаНаНовуюВерсию";
	Обработчик.РежимВыполнения                     = "Отложенно";
	Обработчик.ПроцедураЗаполненияДанныхОбновления = "РегистрыСведений.РезультатыПроверкиУчета.ЗарегистрироватьДанныеКОбработкеДляПереходаНаНовуюВерсию";
	Обработчик.ЧитаемыеОбъекты                     = "РегистрСведений.РезультатыПроверкиУчета";
	Обработчик.ИзменяемыеОбъекты                   = "РегистрСведений.РезультатыПроверкиУчета";
	Обработчик.БлокируемыеОбъекты                  = "РегистрСведений.РезультатыПроверкиУчета";
	Обработчик.ПриоритетыВыполнения                = ОбновлениеИнформационнойБазы.ПриоритетыВыполненияОбработчика();
	Обработчик.ПроцедураПроверки                   = "ОбновлениеИнформационнойБазы.ДанныеОбновленыНаНовуюВерсиюПрограммы";
	Обработчик.Комментарий                         = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
		НСтр("ru = 'Начальное заполнение признака %1 и расчет контрольных сумм в регистре сведений ""Результаты проверки учета"" для повышения производительности.'"),
		"ИгнорироватьПроблему");
	
	Обработчик = Обработчики.Добавить();
	Обработчик.Версия                              = "3.0.1.195";
	Обработчик.Идентификатор                       = Новый УникальныйИдентификатор("fcd45d27-8e5d-45dd-9648-deff60825ae1");
	Обработчик.Процедура                           = "Справочники.ПравилаПроверкиУчета.ОбработатьДанныеДляПереходаНаНовуюВерсию";
	Обработчик.РежимВыполнения                     = "Отложенно";
	Обработчик.ПроцедураЗаполненияДанныхОбновления = "Справочники.ПравилаПроверкиУчета.ЗарегистрироватьДанныеКОбработкеДляПереходаНаНовуюВерсию";
	Обработчик.ЧитаемыеОбъекты                     = "Справочник.ПравилаПроверкиУчета";
	Обработчик.ИзменяемыеОбъекты                   = "Справочник.ПравилаПроверкиУчета";
	Обработчик.БлокируемыеОбъекты                  = "Справочник.ПравилаПроверкиУчета";
	Обработчик.ПриоритетыВыполнения                = ОбновлениеИнформационнойБазы.ПриоритетыВыполненияОбработчика();
	Обработчик.ПроцедураПроверки                   = "ОбновлениеИнформационнойБазы.ДанныеОбновленыНаНовуюВерсиюПрограммы";
	Обработчик.Комментарий                         = НСтр("ru = 'Отключение автоматического запуска системных проверок в подсистеме контроля ведения учета.'");
	
КонецПроцедуры

// Параметры:
//   ТекущиеДела - см. ТекущиеДелаСервер.ТекущиеДела.
//
Процедура ПриЗаполненииСпискаТекущихДел(ТекущиеДела) Экспорт
	
	Если Не ПодсистемаДоступна() Тогда
		Возврат;
	КонецЕсли;
	
	ВсеПроблемы = СводнаяИнформацияПоВидамПроверок(Неопределено, Истина, Истина);
	
	ВидПроверки = ВидПроверки("СистемныеПроверки");
	Проблемы    = СводнаяИнформацияПоВидамПроверок(ВидПроверки, Истина, Истина);
	
	ИнформацияОПоследнейПроверке = ИнформацияОПоследнейПроверкеВеденияУчета();
	
	МодульТекущиеДелаСервер = ОбщегоНазначения.ОбщийМодуль("ТекущиеДелаСервер");
	Разделы                 = МодульТекущиеДелаСервер.РазделыДляОбъекта("Отчет.РезультатыПроверкиУчета");
	ПолноправныйПользователь = Пользователи.ЭтоПолноправныйПользователь();
	
	Для Каждого Раздел Из Разделы Цикл
		
		ИдентификаторВладельца = "КонтрольВеденияУчета" + СтрЗаменить(Раздел.ПолноеИмя(), ".", "_");
		Если ПолноправныйПользователь Тогда
			Дело = ТекущиеДела.Добавить();
			Дело.Идентификатор  = ИдентификаторВладельца;
			Дело.ЕстьДела       = ВсеПроблемы.ЕстьОшибки;
			Дело.Важное         = Ложь;
			Дело.Владелец       = Раздел;
			Дело.Представление  = НСтр("ru = 'Проблемы ведения учета'");
			Дело.Количество     = ВсеПроблемы.Количество;
			Дело.ПараметрыФормы = Новый Структура;
			Дело.Форма          = "Отчет.РезультатыПроверкиУчета.Форма";
		КонецЕсли;
		
		Дело = ТекущиеДела.Добавить();
		Дело.Идентификатор  = "КонтрольВеденияУчетаНекорректныеДанные" + СтрЗаменить(Раздел.ПолноеИмя(), ".", "_");
		Дело.ЕстьДела       = Проблемы.ЕстьОшибки;
		Дело.Важное         = Истина;
		Дело.Владелец       = ?(ПолноправныйПользователь, ИдентификаторВладельца, Раздел);
		Дело.Представление  = НСтр("ru = 'Некорректные данные'");
		Дело.Количество     = Проблемы.Количество;
		Дело.ПараметрыФормы = Новый Структура("ВидПроверки", ВидПроверки);
		Дело.Форма          = "Отчет.РезультатыПроверкиУчета.Форма";
		
		
		// Проверка давно не выполнялась.
		Если ЗначениеЗаполнено(ИнформацияОПоследнейПроверке.ДатаПоследнейПроверки) Тогда
			Подсказка = НСтр("ru = 'Последняя проверка выполнялась %1.'");
			Подсказка = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(Подсказка, 
				Формат(ИнформацияОПоследнейПроверке.ДатаПоследнейПроверки, "ДЛФ=Д"));
		Иначе
			Подсказка = "";
		КонецЕсли;
		Дело = ТекущиеДела.Добавить();
		Дело.Идентификатор  = "КонтрольВеденияУчетаТребуетсяПроверка" + СтрЗаменить(Раздел.ПолноеИмя(), ".", "_");
		Дело.ЕстьДела       = ИнформацияОПоследнейПроверке.ПредупреждатьОНеобходимостиПовторнойПроверки;
		Дело.Важное         = Ложь;
		Дело.Владелец       = Раздел;
		Дело.Представление  = НСтр("ru = 'Проверка ведения учета давно не выполнялась'");
		Дело.Подсказка      = Подсказка;
		Дело.Форма          = "Справочник.ПравилаПроверкиУчета.ФормаСписка";
	КонецЦикла;
	
КонецПроцедуры

// См. ВыгрузкаЗагрузкаДанныхПереопределяемый.ПриЗаполненииТиповИсключаемыхИзВыгрузкиЗагрузки.
Процедура ПриЗаполненииТиповИсключаемыхИзВыгрузкиЗагрузки(Типы) Экспорт
	
	// Результаты проверки ведения учета будут заново сформированы при следующей проверке, 
	// поэтому их не требуется выгружать и загружать.
	Типы.Добавить(Метаданные.РегистрыСведений.РезультатыПроверкиУчета);
	
КонецПроцедуры

// См. ОчередьЗаданийПереопределяемый.ПриПолученииСпискаШаблонов.
Процедура ПриПолученииСпискаШаблонов(ШаблоныЗаданий) Экспорт
	
	ШаблоныЗаданий.Добавить(Метаданные.РегламентныеЗадания.ПроверкаВеденияУчета.Имя);
	
КонецПроцедуры

// См. УправлениеДоступомПереопределяемый.ПриЗаполненииСписковСОграничениемДоступа.
Процедура ПриЗаполненииСписковСОграничениемДоступа(Списки) Экспорт
	
	Списки.Вставить(Метаданные.РегистрыСведений.РезультатыПроверкиУчета, Истина);
	
КонецПроцедуры

// См. ОбщегоНазначенияПереопределяемый.ПриДобавленииПараметровРаботыКлиентаПриЗапуске.
Процедура ПриДобавленииПараметровРаботыКлиентаПриЗапуске(Параметры) Экспорт
	
	Если Не ОбщегоНазначения.ДоступноИспользованиеРазделенныхДанных() Тогда
		Возврат;
	КонецЕсли;
	
	ТекущиеДелаВстроены = ОбщегоНазначения.ПодсистемаСуществует("СтандартныеПодсистемы.ТекущиеДела");
	Количество = 0;
	Если Не Пользователи.ЭтоСеансВнешнегоПользователя() И Не ТекущиеДелаВстроены Тогда
		Количество = СводнаяИнформацияПоВидамПроверок(Неопределено, Истина, Истина).Количество;
	КонецЕсли;
	
	Параметры.Вставить("КонтрольВеденияУчета",
		Новый ФиксированнаяСтруктура("ОповещатьОПроблемахВеденияУчета, КоличествоПроблемВеденияУчета",
			Не ТекущиеДелаВстроены,
			Количество));
	
КонецПроцедуры

// См. ИнтерфейсODataПереопределяемый.ПриЗаполненииЗависимыхТаблицДляВыгрузкиЗагрузкиOData
Процедура ПриЗаполненииЗависимыхТаблицДляВыгрузкиЗагрузкиOData(Таблицы) Экспорт
	
	Таблицы.Добавить(Метаданные.РегистрыСведений.РезультатыПроверкиУчета.ПолноеИмя());
	
КонецПроцедуры

// Обновляет состав системных проверок ведения учета при изменении конфигурации.
// 
// Параметры:
//  ЕстьИзменения - Булево - возвращаемое значение. Если производилась запись, устанавливается Истина, 
//                           иначе не изменяется.
//
Процедура ОбновитьПараметрыПроверокУчета(ЕстьИзменения = Неопределено) Экспорт
	
	УстановитьПривилегированныйРежим(Истина);
	
	КонтрольнаяСуммаПроверок = КонтрольнаяСуммаПроверок();
	
	НачатьТранзакцию();
	Попытка
		ЕстьТекущиеИзменения = Ложь;
		
		СтандартныеПодсистемыСервер.ОбновитьПараметрРаботыПрограммы(
			"СтандартныеПодсистемы.КонтрольВеденияУчета.СистемныеПроверки",
			КонтрольнаяСуммаПроверок, ЕстьТекущиеИзменения);
		
		СтандартныеПодсистемыСервер.ДобавитьИзмененияПараметраРаботыПрограммы(
			"СтандартныеПодсистемы.КонтрольВеденияУчета.СистемныеПроверки",
			?(ЕстьТекущиеИзменения, Новый ФиксированнаяСтруктура("ЕстьИзменения", Истина), Новый ФиксированнаяСтруктура()));
		
		ЗафиксироватьТранзакцию();
	Исключение
		ОтменитьТранзакцию();
		ВызватьИсключение;
	КонецПопытки;
	
	Если ЕстьТекущиеИзменения Тогда
		ЕстьИзменения = Истина;
	КонецЕсли;
	
КонецПроцедуры

Функция ЕстьПроблемыСистемныхПроверок() Экспорт
	
	СводнаяИнформацияПоВидамПроверок = СводнаяИнформацияПоВидамПроверок("СистемныеПроверки");
	Возврат СводнаяИнформацияПоВидамПроверок.Количество > 0;
	
КонецФункции

// Возвращает дату и время последней проверки, а так же необходимость показа предупреждения
// о повторной проверке.
//
Функция ИнформацияОПоследнейПроверкеВеденияУчета(ГруппаПроверок = Неопределено) Экспорт
	
	Результат = Новый Структура;
	Результат.Вставить("ДатаПоследнейПроверки", Неопределено);
	Результат.Вставить("ПредупреждатьОНеобходимостиПовторнойПроверки", Ложь);
	
	Запрос = Новый Запрос(
		"ВЫБРАТЬ
		|	ЕСТЬNULL(МАКСИМУМ(СостоянияПроверокВеденияУчета.ПоследнийЗапуск), Неопределено) КАК ПоследнийЗапуск
		|ИЗ
		|	РегистрСведений.СостоянияПроверокВеденияУчета КАК СостоянияПроверокВеденияУчета
		|		ЛЕВОЕ СОЕДИНЕНИЕ Справочник.ПравилаПроверкиУчета КАК ПравилаПроверкиУчета
		|		ПО СостоянияПроверокВеденияУчета.Проверка = ПравилаПроверкиУчета.Ссылка
		|ГДЕ
		|	&ОтборГруппаПроверок");
	Если ГруппаПроверок = Неопределено Тогда
		ОтборГруппаПроверок = "ИСТИНА";
	Иначе
		ОтборГруппаПроверок = "ПравилаПроверкиУчета.КонтекстПроверокВеденияУчета = &КонтекстПроверокВеденияУчета";
	КонецЕсли;	
	Запрос.Текст = СтрЗаменить(Запрос.Текст, "&ОтборГруппаПроверок", ОтборГруппаПроверок);
	Запрос.УстановитьПараметр("КонтекстПроверокВеденияУчета", "СистемныеПроверки");
	УстановитьПривилегированныйРежим(Истина);
	РезультатЗапроса = Запрос.Выполнить().Выгрузить();
	УстановитьПривилегированныйРежим(Ложь);
	
	Результат.ДатаПоследнейПроверки = РезультатЗапроса[0].ПоследнийЗапуск;
	Если Результат.ДатаПоследнейПроверки = Неопределено Тогда
		Результат.ПредупреждатьОНеобходимостиПовторнойПроверки = Истина;
	Иначе
		ВремяСПоследнегоЗапуска = (ТекущаяДатаСеанса() - Результат.ДатаПоследнейПроверки) / (1000 * 60 * 60 * 30);
		Результат.ПредупреждатьОНеобходимостиПовторнойПроверки = ВремяСПоследнегоЗапуска > 1; // Больше 1 месяца.
	КонецЕсли;
	
	Возврат Результат;
	
КонецФункции

Функция ИнформацияОСистемныхПроверкахВеденияУчета() Экспорт
	
	Возврат ИнформацияОПоследнейПроверкеВеденияУчета("СистемныеПроверки");
	
КонецФункции

Функция ПоследняяПроверкаОбъекта(Ссылка) Экспорт
	
	УстановитьПривилегированныйРежим(Истина);
	Набор = РегистрыСведений.РезультатыПроверкиУчета.СоздатьНаборЗаписей();
	Набор.Отбор.ПроблемныйОбъект.Установить(Ссылка);
	Набор.Прочитать();
	УстановитьПривилегированныйРежим(Ложь);
	
	Данные = Набор.Выгрузить(, "Выявлено");
	Данные.Сортировать("Выявлено Убыв");
	
	Если Данные.Количество() = 0 Тогда
		Возврат Неопределено;
	КонецЕсли;
	
	Возврат Данные[0].Выявлено;
	
КонецФункции

// Подготавливает данные, необходимые для выполнения проверки ведения учета.
//
// Параметры:
//     Проверка - СправочникСсылка.ПравилаПроверкиУчета - проверка, параметры которой
//                необходимо подготовить.
//     ПараметрыВыполненияПроверки - Структура
//                                 - Массив - произвольные дополнительные параметры проверки,
//                                   которые уточняют, что и как именно проверять. 
//                                 - Структура - один параметр проверки. Состав свойств см. КонтрольВеденияУчета.ПараметрыВыполненияПроверки.
//                                 - Массив - несколько параметров проверки (элементы массива типа Структура, как
//                                            описано выше).
//
// Возвращаемое значение:
//   см. КонтрольВеденияУчета.ОписаниеПроблемы.
//
Функция ПодготовитьПараметрыПроверки(Знач Проверка, Знач ПараметрыВыполненияПроверки) Экспорт
	
	ПараметрыПроверки = ОбщегоНазначения.ЗначенияРеквизитовОбъекта(Проверка, "ДатаНачалаПроверки, Идентификатор,
		|ИдентификаторРегламентногоЗадания, ЛимитПроблем, Наименование, ВажностьПроблемы, СпособВыполнения,
		|КонтекстПроверокВеденияУчета, УточнениеКонтекстаПроверокВеденияУчета");
	
	Если Не ПараметрыПроверки.Свойство("ПроверкаБылаОстановлена") Тогда
		ПараметрыПроверки.Вставить("ПроверкаБылаОстановлена", Ложь);
	КонецЕсли;
	Если Не ПараметрыПроверки.Свойство("РучнойЗапуск") Тогда
		ПараметрыПроверки.Вставить("РучнойЗапуск", Истина);
	КонецЕсли;
	
	ПроверкиВеденияУчета = КонтрольВеденияУчетаСлужебныйПовтИсп.ПроверкиВеденияУчета().Проверки;
	ВсеСвойстваПроверки  = ПроверкиВеденияУчета.Найти(ПараметрыПроверки.Идентификатор, "Идентификатор");
	
	ПараметрыПроверки.Вставить("Проверка",            Проверка);
	ПараметрыПроверки.Вставить("ГлобальныеНастройки", ГлобальныеНастройки());
	ПараметрыПроверки.Вставить("ИтерацияПроверки",    1);
	Если ВсеСвойстваПроверки <> Неопределено Тогда
		ПараметрыПроверки.Вставить("ПоддерживаетВыборочнуюПроверку", ВсеСвойстваПроверки.ПоддерживаетВыборочнуюПроверку);
	КонецЕсли;
	
	Если ПараметрыВыполненияПроверки = Неопределено Тогда
		ГруппаПроверок = ОбщегоНазначения.ЗначениеРеквизитаОбъекта(Проверка, "Родитель");
		Если ГруппаПроверок <> Неопределено И Не ГруппаПроверок.Пустая() Тогда
			ИдентификаторПроверки = ОбщегоНазначения.ЗначениеРеквизитаОбъекта(ГруппаПроверок, "Идентификатор");
		Иначе
			ИдентификаторПроверки = ПараметрыПроверки.Идентификатор;
		КонецЕсли;
		ПараметрыВыполненияПроверки = Новый Массив;
		ПараметрыВыполненияПроверки.Добавить(ПараметрыВыполненияПроверки(ИдентификаторПроверки));
	КонецЕсли;
	ПараметрыПроверки.Вставить("ПараметрыВыполненияПроверки", ПараметрыВыполненияПроверки);
	
	Возврат ПараметрыПроверки;
	
КонецФункции

// Обновляет вспомогательные данные, которые зависят от конфигурации частично.
//
Процедура ОбновитьВспомогательныеДанныеПоИзменениямКонфигурации(Параметры = Неопределено) Экспорт
	
	НачатьТранзакцию();
	Попытка
		Блокировка = Новый БлокировкаДанных;
		Блокировка.Добавить("Справочник.ПравилаПроверкиУчета");
		Блокировка.Заблокировать();
		
		ОбновитьВспомогательныеДанныеСправочникаПоИзменениямКонфигурации();
		ЗафиксироватьТранзакцию();
	Исключение
		ОтменитьТранзакцию();
		ВызватьИсключение;
	КонецПопытки;
	
КонецПроцедуры

#КонецОбласти

#Область СлужебныеПроцедурыИФункции

// См. КонтрольВеденияУчета.СводнаяИнформацияПоВидамПроверок.
Функция СводнаяИнформацияПоВидамПроверок(ВидПроверок, ПоискПоТочномуСоответствию = Истина, УчитыватьОтветственного = Ложь) Экспорт
	
	СводнаяИнформация = Новый Структура;
	СводнаяИнформация.Вставить("Количество", 0);
	СводнаяИнформация.Вставить("ЕстьОшибки", Ложь);
	
	Если Не ПравоДоступа("Чтение", Метаданные.РегистрыСведений.РезультатыПроверкиУчета) Тогда
		Возврат СводнаяИнформация;
	КонецЕсли;
	
	ВидыПроверок = Новый Массив;
	
	Если ТипЗнч(ВидПроверок) = Тип("СправочникСсылка.ВидыПроверок") Тогда
		ВидыПроверок.Добавить(ВидПроверок);
	ИначеЕсли ТипЗнч(ВидПроверок) = Тип("Строка") Тогда
		ПараметрыВыполненияПроверки = ПараметрыВыполненияПроверки(ВидПроверок);
		ВидыПроверок = ВидыПроверок(ПараметрыВыполненияПроверки, ПоискПоТочномуСоответствию);
	ИначеЕсли ВидПроверок <> Неопределено Тогда
		ПараметрыВыполненияПроверки = ПараметрыВыполненияПроверкиИзМассива(ВидПроверок);
		ВидыПроверок = ВидыПроверок(ПараметрыВыполненияПроверки, ПоискПоТочномуСоответствию);
	КонецЕсли;
	
	Запрос = Новый Запрос;
	Запрос.Текст = 
		"ВЫБРАТЬ РАЗРЕШЕННЫЕ
		|	КОЛИЧЕСТВО(*) КАК Количество,
		|	ЕСТЬNULL(МАКСИМУМ(ВЫБОР
		|				КОГДА РезультатыПроверкиУчета.ВажностьПроблемы = ЗНАЧЕНИЕ(Перечисление.ВажностьПроблемыУчета.Ошибка)
		|					ТОГДА ИСТИНА
		|				ИНАЧЕ ЛОЖЬ
		|			КОНЕЦ), ЛОЖЬ) КАК ЕстьОшибки
		|ИЗ
		|	РегистрСведений.РезультатыПроверкиУчета КАК РезультатыПроверкиУчета
		|ГДЕ
		|	РезультатыПроверкиУчета.ПравилоПроверки.ПометкаУдаления = ЛОЖЬ
		|	И НЕ РезультатыПроверкиУчета.ИгнорироватьПроблему";
	Если ВидПроверок <> Неопределено Тогда
		Запрос.Текст = Запрос.Текст + " И РезультатыПроверкиУчета.ВидПроверки В (&ВидыПроверок)";
		Запрос.УстановитьПараметр("ВидыПроверок", ВидыПроверок);
	КонецЕсли;
	Если УчитыватьОтветственного Тогда
		Запрос.Текст = Запрос.Текст + " И РезультатыПроверкиУчета.Ответственный В (&Ответственный)";
		Ответственный = Новый Массив;
		Ответственный.Добавить(Справочники.Пользователи.ПустаяСсылка());
		Ответственный.Добавить(Пользователи.ТекущийПользователь());
		Запрос.УстановитьПараметр("Ответственный", Ответственный);
	КонецЕсли;
	Результат = Запрос.Выполнить().Выбрать();
	Результат.Следующий();
	
	ЗаполнитьЗначенияСвойств(СводнаяИнформация, Результат);
	Возврат СводнаяИнформация;
	
КонецФункции

// См. КонтрольВеденияУчета.ПодробнаяИнформацияПоВидамПроверок.
Функция ПодробнаяИнформацияПоВидамПроверок(ВидПроверок, ПоискПоТочномуСоответствию = Истина) Экспорт
	
	ПодробнаяИнформация        = Новый ТаблицаЗначений;
	КолонкиПодробнойИнформации = ПодробнаяИнформация.Колонки;
	КолонкиПодробнойИнформации.Добавить("ПроблемныйОбъект",         ОбщегоНазначения.ОписаниеТипаВсеСсылки());
	КолонкиПодробнойИнформации.Добавить("ВажностьПроблемы",         Новый ОписаниеТипов("ПеречислениеСсылка.ВажностьПроблемыУчета"));
	КолонкиПодробнойИнформации.Добавить("ПравилоПроверки",          Новый ОписаниеТипов("СправочникСсылка.ПравилаПроверкиУчета"));
	КолонкиПодробнойИнформации.Добавить("ВидПроверки",              Новый ОписаниеТипов("СправочникСсылка.ВидыПроверок"));
	КолонкиПодробнойИнформации.Добавить("УточнениеПроблемы",        Новый ОписаниеТипов("Строка"));
	КолонкиПодробнойИнформации.Добавить("Ответственный",            Новый ОписаниеТипов("СправочникСсылка.Пользователи"));
	КолонкиПодробнойИнформации.Добавить("Выявлено",                 Новый ОписаниеТипов("Дата"));
	КолонкиПодробнойИнформации.Добавить("ДополнительнаяИнформация", Новый ОписаниеТипов("ХранилищеЗначения"));
	
	ВидыПроверок = Новый Массив;
	
	Если ТипЗнч(ВидПроверок) = Тип("СправочникСсылка.ВидыПроверок") Тогда
		ВидыПроверок.Добавить(ВидПроверок);
	ИначеЕсли ТипЗнч(ВидПроверок) = Тип("Строка") Тогда
		ПараметрыВыполненияПроверки = ПараметрыВыполненияПроверки(ВидПроверок);
		ВидыПроверок = ВидыПроверок(ПараметрыВыполненияПроверки, ПоискПоТочномуСоответствию);
	Иначе
		ПараметрыВыполненияПроверки = ПараметрыВыполненияПроверкиИзМассива(ВидПроверок);
		ВидыПроверок = ВидыПроверок(ПараметрыВыполненияПроверки, ПоискПоТочномуСоответствию);
	КонецЕсли;
	
	Если ВидыПроверок.Количество() = 0 Тогда
		Возврат ПодробнаяИнформация;
	КонецЕсли;
	
	Запрос = Новый Запрос(
	"ВЫБРАТЬ РАЗРЕШЕННЫЕ
	|	РезультатыПроверкиУчета.ПроблемныйОбъект КАК ПроблемныйОбъект,
	|	РезультатыПроверкиУчета.ПравилоПроверки КАК ПравилоПроверки,
	|	РезультатыПроверкиУчета.ВажностьПроблемы КАК ВажностьПроблемы,
	|	РезультатыПроверкиУчета.ВидПроверки КАК ВидПроверки,
	|	РезультатыПроверкиУчета.УточнениеПроблемы КАК УточнениеПроблемы,
	|	РезультатыПроверкиУчета.Ответственный КАК Ответственный,
	|	РезультатыПроверкиУчета.Выявлено КАК Выявлено,
	|	РезультатыПроверкиУчета.ДополнительнаяИнформация КАК ДополнительнаяИнформация
	|ИЗ
	|	РегистрСведений.РезультатыПроверкиУчета КАК РезультатыПроверкиУчета
	|ГДЕ
	|	РезультатыПроверкиУчета.ПравилоПроверки.ПометкаУдаления = ЛОЖЬ
	|	И НЕ РезультатыПроверкиУчета.ИгнорироватьПроблему
	|	И РезультатыПроверкиУчета.ВидПроверки В(&ВидыПроверок)");
	
	Запрос.УстановитьПараметр("ВидыПроверок", ВидыПроверок);
	Результат = Запрос.Выполнить();
	
	Если Не Результат.Пустой() Тогда
		ПодробнаяИнформация = Результат.Выгрузить();
	КонецЕсли;
	
	Возврат ПодробнаяИнформация;
	
КонецФункции

// См. КонтрольВеденияУчета.ВидыПроверок.
Функция ВидыПроверок(ВидПроверок, ПоискПоТочномуСоответствию = Истина) Экспорт
	
	Если ТипЗнч(ВидПроверок) = Тип("СправочникСсылка.ВидыПроверок") Тогда
		Результат = Новый Массив;
		Результат.Добавить(ВидПроверок);
		Возврат Результат;
	КонецЕсли;
	
	Если ТипЗнч(ВидПроверок) = Тип("Строка") Тогда
		ПараметрыВыполненияПроверки = ПараметрыВыполненияПроверки(ВидПроверок);
	ИначеЕсли ТипЗнч(ВидПроверок) = Тип("Массив") Тогда
		ПараметрыВыполненияПроверки = ПараметрыВыполненияПроверкиИзМассива(ВидПроверок);
	Иначе
		ПараметрыВыполненияПроверки = ВидПроверок;
	КонецЕсли;
	
	Если ТипЗнч(ПараметрыВыполненияПроверки) = Тип("Структура") Тогда
		Возврат ОбычныйПоискВидаПроверки(ПараметрыВыполненияПроверки, ПоискПоТочномуСоответствию);
		
	ИначеЕсли ТипЗнч(ПараметрыВыполненияПроверки) = Тип("Массив") Тогда
		
		Если ПараметрыВыполненияПроверки.Количество() > КоличествоСвойств() Тогда
			Возврат РасширенныйПоискВидаПроверки(ПараметрыВыполненияПроверки, ПараметрыВыполненияПроверки.Количество());
		Иначе
			Возврат ОбычныйПоискВидаПроверки(ПараметрыВыполненияПроверки, ПоискПоТочномуСоответствию);
		КонецЕсли;
		
	КонецЕсли;
	
	Возврат Новый Массив;
	
КонецФункции

// См. КонтрольВеденияУчета.ВидПроверки.
Функция ВидПроверки(Знач ПараметрыВыполненияПроверки, Знач ТолькоПоиск = Ложь) Экспорт
	
	Если ТипЗнч(ПараметрыВыполненияПроверки) = Тип("Строка") Тогда
		ПараметрыВыполненияПроверки = ПараметрыВыполненияПроверки(ПараметрыВыполненияПроверки);
	КонецЕсли;
	
	НачатьТранзакцию();
	Попытка
		Блокировка = БлокировкаВидовПроверок(ПараметрыВыполненияПроверки);
		Блокировка.Заблокировать();
		
		Если ПараметрыВыполненияПроверки.Количество() - 1 > КоличествоСвойств() Тогда
			ВидПроверкиМассив = РасширенныйПоискВидаПроверки(ПараметрыВыполненияПроверки, КоличествоСвойств());
		Иначе
			ВидПроверкиМассив = ОбычныйПоискВидаПроверки(ПараметрыВыполненияПроверки);
		КонецЕсли;
		
		Если ВидПроверкиМассив.Количество() = 0 Тогда
			Если ТолькоПоиск Тогда
				ВидПроверки = Справочники.ВидыПроверок.ПустаяСсылка();
			Иначе
				ВидПроверки = НовыйВидПроверки(ПараметрыВыполненияПроверки);
			КонецЕсли;
		Иначе
			ВидПроверки = ВидПроверкиМассив.Получить(0);
		КонецЕсли;
		
		ЗафиксироватьТранзакцию();
	Исключение
		ОтменитьТранзакцию();
		ВызватьИсключение;
	КонецПопытки;
	
	Возврат ВидПроверки;
	
КонецФункции

// Производит поиск вида проверки по переданным параметрам. 
//
// Параметры:
//   ПараметрыВыполненияПроверки - см. КонтрольВеденияУчета.ПараметрыВыполненияПроверки.
//   КоличествоСвойств           - Число - количество свойств, по которым нужно вести поиск.
//
// Возвращаемое значение: 
//   СправочникСсылка.ВидыПроверок - элемент справочника, либо пустая ссылка, в случае если поиск не дал результата.
//
Функция РасширенныйПоискВидаПроверки(ПараметрыВыполненияПроверки, КоличествоСвойств)
	
	Запрос = Новый Запрос(
	"ВЫБРАТЬ
	|	ВидыПроверок.Ссылка КАК ВидПроверки
	|ПОМЕСТИТЬ ВТ_ВидыПроверок
	|ИЗ
	|	Справочник.ВидыПроверок КАК ВидыПроверок
	|ГДЕ
	|	&Условие
	|
	|СГРУППИРОВАТЬ ПО
	|	ВидыПроверок.Ссылка
	|
	|ИМЕЮЩИЕ
	|	КОЛИЧЕСТВО(ВидыПроверок.СвойстваОбъекта.Ссылка) = &ПороговоеКоличество
	|;
	|
	|////////////////////////////////////////////////////////////////////////////////
	|ВЫБРАТЬ
	|	ВидыПроверокСвойстваОбъекта.Ссылка КАК ВидПроверки,
	|	ВидыПроверокСвойстваОбъекта.ЗначениеСвойства КАК ЗначениеСвойства,
	|	ВидыПроверокСвойстваОбъекта.ИмяСвойства КАК ИмяСвойства
	|ИЗ
	|	ВТ_ВидыПроверок КАК ВТ_ВидыПроверок
	|		ВНУТРЕННЕЕ СОЕДИНЕНИЕ Справочник.ВидыПроверок.СвойстваОбъекта КАК ВидыПроверокСвойстваОбъекта
	|		ПО ВТ_ВидыПроверок.ВидПроверки = ВидыПроверокСвойстваОбъекта.Ссылка
	|
	|УПОРЯДОЧИТЬ ПО
	|	ВидПроверки");
	
	ТекстУсловий           = " Истина ";
	КоличествоПараметров   = ПараметрыВыполненияПроверки.Количество() - 1;
	Запрос.УстановитьПараметр("ПороговоеКоличество", КоличествоПараметров - КоличествоСвойств);
	
	Для Индекс = 1 По КоличествоСвойств Цикл
		
		Свойство  = "Свойство" + Формат(Индекс, "ЧГ=0");
		Значение  = ПараметрыВыполненияПроверки[Свойство];
		
		ТекстУсловий = ТекстУсловий + " И " + Свойство + " = &" + Свойство;
		Запрос.УстановитьПараметр(Свойство, Значение);
		
	КонецЦикла;
	
	Запрос.Текст = СтрЗаменить(Запрос.Текст, "&Условие", ТекстУсловий);
	Результат = Запрос.Выполнить().Выгрузить();
	
	ТранспонированнаяТаблица = Новый ТаблицаЗначений;
	КолонкиТаблицы           = ТранспонированнаяТаблица.Колонки;
	КолонкиТаблицы.Добавить("ВидПроверки", Новый ОписаниеТипов("СправочникСсылка.ВидыПроверок"));
	
	СтруктураПоиска = Новый Структура;
	ИндексПоиска    = "";
	
	Для ПороговыйИндекс = КоличествоСвойств + 1 По КоличествоПараметров Цикл
		
		ИмяКолонки   = "Свойство" + Формат(ПороговыйИндекс, "ЧГ=0");
		ИндексПоиска = ИндексПоиска + ?(ЗначениеЗаполнено(ИндексПоиска), ", ", "") + ИмяКолонки;
		КолонкиТаблицы.Добавить(ИмяКолонки);
		
		СтруктураПоиска.Вставить(ИмяКолонки, ПараметрыВыполненияПроверки[ИмяКолонки]);
		
	КонецЦикла;
	
	ТекущийВидПроверки = Неопределено;
	Для Каждого СтрокаРезультата Из Результат Цикл
		
		Если ТекущийВидПроверки <> СтрокаРезультата.ВидПроверки Тогда
			
			ТекущийВидПроверки = СтрокаРезультата.ВидПроверки;
			НоваяСтрока = ТранспонированнаяТаблица.Добавить();
			НоваяСтрока.ВидПроверки = ТекущийВидПроверки;
			
		КонецЕсли;
		
		НоваяСтрока[СтрокаРезультата.ИмяСвойства] = СтрокаРезультата.ЗначениеСвойства;
		
	КонецЦикла;
	
	Если ТранспонированнаяТаблица.Количество() > 1000 Тогда
		ТранспонированнаяТаблица.Индексы.Добавить(ИндексПоиска);
	КонецЕсли;
	
	НайденныеСтроки     = ТранспонированнаяТаблица.НайтиСтроки(СтруктураПоиска);
	МассивВидовПроверок = Новый Массив;
	Для Каждого НайденнаяСтрока Из НайденныеСтроки Цикл
		МассивВидовПроверок.Добавить(НайденнаяСтрока.ВидПроверки);
	КонецЦикла;
	
	Возврат МассивВидовПроверок;
	
КонецФункции

// Во избежание конфликтов при выполнении разными регламентными заданиями, производит блокировку 
// справочника ВидыПроверки по переданным параметрам выполнения проверки.
//
// Параметры:
//   ПараметрыВыполненияПроверки - см. КонтрольВеденияУчета.ПараметрыВыполненияПроверки.
//
// Возвращаемое значение:
//   БлокировкаДанных  - объект блокировки справочника ВидыПроверки.
//
Функция БлокировкаВидовПроверок(ПараметрыВыполненияПроверки)
	
	БлокировкаДанных = Новый БлокировкаДанных;
	ЭлементБлокировкиДанных = БлокировкаДанных.Добавить("Справочник.ВидыПроверок");
	
	Если ПараметрыВыполненияПроверки.Количество() - 1 > КоличествоСвойств() Тогда
		Индекс = 1;
		Для Каждого ПараметрПоиска Из ПараметрыВыполненияПроверки Цикл
			ЭлементБлокировкиДанных.УстановитьЗначение("Свойство" + Формат(Индекс, "ЧГ=0"), ПараметрПоиска.Значение);
			Индекс = Индекс + 1;
		КонецЦикла;
	КонецЕсли;
	
	Возврат БлокировкаДанных;
	
КонецФункции

// Производит поиск вида проверки по переданным параметрам. 
//
// Параметры:
//   ПараметрыВыполненияПроверки - см. КонтрольВеденияУчета.ПараметрыВыполненияПроверки.
//   ПоискПоТочномуСоответствию - Булево - если Истина, то поиск ведется
//                                по переданным свойствам на равенство, остальные свойства должны быть равны
//                                Неопределено (табличная часть дополнительных свойств должна быть пуста).
//                                Если Ложь, то значения остальных свойств могут быть произвольными, главное
//                                чтобы соответствующие свойства были равны свойствам структуры. По умолчанию Истина.
//
// Возвращаемое значение: 
//   СправочникСсылка.ВидыПроверок - элемент справочника, либо пустая ссылка, если поиск не дал результата.
//
Функция ОбычныйПоискВидаПроверки(ПараметрыВыполненияПроверки, ПоискПоТочномуСоответствию = Истина)
	
	Запрос = Новый Запрос(
	"ВЫБРАТЬ
	|	ВидыПроверок.Ссылка КАК ВидПроверки
	|ИЗ
	|	Справочник.ВидыПроверок КАК ВидыПроверок
	|ГДЕ
	|	&Условие
	|
	|СГРУППИРОВАТЬ ПО
	|	ВидыПроверок.Ссылка
	|
	|ИМЕЮЩИЕ
	|	КОЛИЧЕСТВО(ВидыПроверок.СвойстваОбъекта.Ссылка) = 0");
	
	ТекстУсловий         = " Истина ";
	КоличествоПараметров = ПараметрыВыполненияПроверки.Количество() - 1;
	КоличествоСвойств    = КоличествоСвойств();
	
	Для Индекс = 1 По КоличествоСвойств Цикл
		
		Свойство = "Свойство" + Формат(Индекс, "ЧГ=0");
		Если Индекс > КоличествоПараметров Тогда
			Если ПоискПоТочномуСоответствию Тогда
				ТекстУсловий = ТекстУсловий + " И " + Свойство + " = Неопределено";
			КонецЕсли;
		Иначе
			Значение     = ПараметрыВыполненияПроверки[Свойство];
			
			ТекстУсловий = ТекстУсловий + " И " + Свойство + " = &" + Свойство;
			Запрос.УстановитьПараметр(Свойство, Значение);
		КонецЕсли;
		
	КонецЦикла;
	
	Запрос.Текст = СтрЗаменить(Запрос.Текст, "&Условие", ТекстУсловий);
	Результат = Запрос.Выполнить().Выгрузить();
	
	Возврат Результат.ВыгрузитьКолонку("ВидПроверки");
	
КонецФункции

// Создает элемент справочника ВидыПроверок по переданным параметрам.
//
// Параметры:
//   ПараметрыВыполненияПроверки - см. КонтрольВеденияУчета.ПараметрыВыполненияПроверки.
//
// Возвращаемое значение: 
//    СправочникСсылка.ВидыПроверок - созданный элемент справочника.
//
Функция НовыйВидПроверки(ПараметрыВыполненияПроверки)
	
	ПроверкиВеденияУчета = КонтрольВеденияУчетаСлужебныйПовтИсп.ПроверкиВеденияУчета();
	ГруппыПроверок = ПроверкиВеденияУчета.ГруппыПроверок;
	ГруппаПроверок = ГруппыПроверок.Найти(ПараметрыВыполненияПроверки.Наименование, "Идентификатор");
	
	НовыйВидПроверки = Справочники.ВидыПроверок.СоздатьЭлемент();
	Если ГруппаПроверок = Неопределено Тогда
		НовыйВидПроверки.Наименование = ПараметрыВыполненияПроверки.Наименование;
	Иначе
		НовыйВидПроверки.Наименование = ГруппаПроверок.Наименование;
	КонецЕсли;
	КоличествоСвойств    = КоличествоСвойств();
	КоличествоПараметров = ПараметрыВыполненияПроверки.Количество() - 1;
	
	Если КоличествоСвойств > КоличествоПараметров Тогда
		Для Индекс = 1 По КоличествоПараметров Цикл
			ИмяСвойства = "Свойство" + Формат(Индекс, "ЧГ=0");
			НовыйВидПроверки[ИмяСвойства] = ПараметрыВыполненияПроверки[ИмяСвойства];
		КонецЦикла;
	Иначе
		Для Индекс = 1 По КоличествоПараметров Цикл
			ИмяСвойства = "Свойство" + Формат(Индекс, "ЧГ=0");
			Если Индекс <= КоличествоСвойств Тогда
				НовыйВидПроверки[ИмяСвойства] = ПараметрыВыполненияПроверки[ИмяСвойства];
			Иначе
				ЗаполнитьЗначенияСвойств(НовыйВидПроверки.СвойстваОбъекта.Добавить(),
					Новый Структура("ИмяСвойства, ЗначениеСвойства", ИмяСвойства, ПараметрыВыполненияПроверки[ИмяСвойства]));
			КонецЕсли;
		КонецЦикла;
	КонецЕсли;
	
	НовыйВидПроверки.Записать();
	
	Возврат НовыйВидПроверки.Ссылка;
	
КонецФункции

// Количество свойств в шапке справочника ВидыПроверок.
// 
// Возвращаемое значение: 
//   Число - значение 5.
//
Функция КоличествоСвойств()
	
	Возврат 5;
	
КонецФункции

// См. КонтрольВеденияУчетаПереопределяемый.ПриОпределенииНастроек.
Функция ГлобальныеНастройки() Экспорт
	
	Настройки = Новый Структура;
	Настройки.Вставить("КартинкаИндикатораПроблем",    БиблиотекаКартинок.Предупреждение);
	Настройки.Вставить("ПояснениеИндикатораПроблем",   Неопределено);
	Настройки.Вставить("ГиперссылкаИндикатораПроблем", Неопределено);
	
	КонтрольВеденияУчетаПереопределяемый.ПриОпределенииНастроек(Настройки);
	
	Возврат Настройки;
	
КонецФункции

// Возвращает массив проблемных объектов. Максимально "облегчена" для прироста производительности.
//
//  Параметры:
//    КлючиСтрок - Массив - массив, содержащий все ключи строк динамического списка.
//
// Возвращаемое значение:
//   Массив из ЛюбаяСсылка - массив проблемных объектов.
//
Функция ПроблемныеОбъекты(КлючиСтрок, ВключаяВажность = Ложь) Экспорт
	
	ТекущийПользовательПолноправный = Пользователи.ЭтоПолноправныйПользователь();
	
	Запрос = Новый Запрос(
	"ВЫБРАТЬ РАЗЛИЧНЫЕ
	|	РезультатыПроверкиУчета.ПроблемныйОбъект КАК ПроблемныйОбъект,
	|	МИНИМУМ(РезультатыПроверкиУчета.ВажностьПроблемы.Порядок) КАК ПорядокПроблемы
	|ИЗ
	|	РегистрСведений.РезультатыПроверкиУчета КАК РезультатыПроверкиУчета
	|ГДЕ
	|	РезультатыПроверкиУчета.ПроблемныйОбъект В(&СписокОбъектов)
	|	И НЕ РезультатыПроверкиУчета.ИгнорироватьПроблему
	|
	|СГРУППИРОВАТЬ ПО
	|	РезультатыПроверкиУчета.ПроблемныйОбъект");
	Запрос.УстановитьПараметр("СписокОбъектов", КлючиСтрок);
	
	Если Не ТекущийПользовательПолноправный Тогда
		УстановитьПривилегированныйРежим(Истина);
	КонецЕсли;
	
	Если ВключаяВажность Тогда
		ПроблемныеОбъекты = Запрос.Выполнить().Выгрузить();
	Иначе
		ПроблемныеОбъекты = Запрос.Выполнить().Выгрузить().ВыгрузитьКолонку("ПроблемныйОбъект");
	КонецЕсли;
	
	Если Не ТекущийПользовательПолноправный Тогда
		УстановитьПривилегированныйРежим(Ложь);
	КонецЕсли;
	
	Возврат ПроблемныеОбъекты;
	
КонецФункции

// См. КонтрольВеденияУчета.ОписаниеПроблемы.
Функция ОписаниеПроблемы(ПроблемныйОбъект, ПараметрыПроверки) Экспорт
	
	Результат = Новый Структура;
	Результат.Вставить("ПроблемныйОбъект",         ПроблемныйОбъект);
	Результат.Вставить("ПравилоПроверки",          ПараметрыПроверки.Проверка);
	Результат.Вставить("ВажностьПроблемы",         ПараметрыПроверки.ВажностьПроблемы);
	Результат.Вставить("УточнениеПроблемы",        "");
	Результат.Вставить("КлючУникальности",         Новый УникальныйИдентификатор);
	Результат.Вставить("Выявлено",                 ТекущаяДатаСеанса());
	Результат.Вставить("ДополнительнаяИнформация", Новый ХранилищеЗначения(Неопределено));
	Результат.Вставить("Ответственный",            Неопределено);
	Результат.Вставить("ВидПроверки",              ?(ПараметрыПроверки.ПараметрыВыполненияПроверки.Количество() = 1,
		ВидПроверки(ПараметрыПроверки.ПараметрыВыполненияПроверки[0]), Неопределено));
	
	Возврат Результат;
	
КонецФункции

// См. КонтрольВеденияУчета.ЗаписатьПроблему.
Процедура ЗаписатьПроблему(ОшибкаПроверки, ПараметрыПроверки = Неопределено) Экспорт
	
	Если ПараметрыПроверки <> Неопределено И ЭтоПоследняяИтерацияПроверки(ПараметрыПроверки) Тогда
		Возврат;
	КонецЕсли;
	
	Если Не ЗначениеЗаполнено(ОшибкаПроверки.ВидПроверки) Тогда
		ВызватьИсключение СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
			НСтр("ru = 'При записи проблемы по проверке ""%1"" не указан вид проверки.'"), 
				ОшибкаПроверки.ПравилоПроверки);
	КонецЕсли;
	
	ИсключаемыеОбъекты = КонтрольВеденияУчетаСлужебныйПовтИсп.ИсключаемыеИзПроверкиОбъекты();
	
	ПроблемныйОбъект = ОшибкаПроверки.ПроблемныйОбъект;
	ОбъектМетаданных = ПроблемныйОбъект.Метаданные();
	
	Если ИсключаемыеОбъекты.Найти(ОбъектМетаданных.ПолноеИмя()) <> Неопределено Тогда
		Возврат;
	КонецЕсли;
	
	КоллекцияРеквизитов = ОбъектМетаданных.Реквизиты;
	
	КонтрольВеденияУчетаПереопределяемый.ПередЗаписьюПроблемы(ОшибкаПроверки, ПроблемныйОбъект, КоллекцияРеквизитов);
	
	ОшибкаПроверки.Вставить("КонтрольнаяСумма", КонтрольнаяСуммаПроблемы(ОшибкаПроверки));
	Если ЭтаПроблемаИгнорируется(ОшибкаПроверки.КонтрольнаяСумма) Тогда
		Возврат;
	КонецЕсли;
	
	НаборЗаписей = РегистрыСведений.РезультатыПроверкиУчета.СоздатьНаборЗаписей();
	Отбор = НаборЗаписей.Отбор;
	Отбор.ПроблемныйОбъект.Установить(ПроблемныйОбъект);
	Отбор.ПравилоПроверки.Установить(ОшибкаПроверки.ПравилоПроверки);
	Отбор.ВидПроверки.Установить(ОшибкаПроверки.ВидПроверки);
	Отбор.КлючУникальности.Установить(ОшибкаПроверки.КлючУникальности);
	
	НоваяЗапись = НаборЗаписей.Добавить();
	ЗаполнитьЗначенияСвойств(НоваяЗапись, ОшибкаПроверки);
	
	УстановитьПривилегированныйРежим(Истина);
	НаборЗаписей.Записать();
	УстановитьПривилегированныйРежим(Ложь);
	
КонецПроцедуры

// См. КонтрольВеденияУчета.ИгнорироватьПроблему.
Процедура ИгнорироватьПроблему(ОписаниеПроблемы, Значение) Экспорт
	
	НачатьТранзакцию();
	Попытка
		БлокировкаДанных        = Новый БлокировкаДанных;
		ЭлементБлокировкиДанных = БлокировкаДанных.Добавить("РегистрСведений.РезультатыПроверкиУчета");
		ЭлементБлокировкиДанных.УстановитьЗначение("ПроблемныйОбъект", ОписаниеПроблемы.ПроблемныйОбъект);
		ЭлементБлокировкиДанных.УстановитьЗначение("ПравилоПроверки", ОписаниеПроблемы.ПравилоПроверки);
		ЭлементБлокировкиДанных.УстановитьЗначение("ВидПроверки", ОписаниеПроблемы.ВидПроверки);
		БлокировкаДанных.Заблокировать();
		
		КонтрольнаяСумма = КонтрольнаяСуммаПроблемы(ОписаниеПроблемы);
		
		Запрос = Новый Запрос(
		"ВЫБРАТЬ
		|	РезультатыПроверкиУчета.ИгнорироватьПроблему КАК ИгнорироватьПроблему,
		|	РезультатыПроверкиУчета.ПроблемныйОбъект КАК ПроблемныйОбъект,
		|	РезультатыПроверкиУчета.ПравилоПроверки КАК ПравилоПроверки,
		|	РезультатыПроверкиУчета.ВидПроверки КАК ВидПроверки,
		|	РезультатыПроверкиУчета.ДополнительнаяИнформация КАК ДополнительнаяИнформация
		|ИЗ
		|	РегистрСведений.РезультатыПроверкиУчета КАК РезультатыПроверкиУчета
		|ГДЕ
		|	РезультатыПроверкиУчета.КонтрольнаяСумма = &КонтрольнаяСумма");
		
		Запрос.УстановитьПараметр("КонтрольнаяСумма", КонтрольнаяСумма);
		Результат = Запрос.Выполнить();
		Если Результат.Пустой() Тогда
			ОтменитьТранзакцию();
			Возврат;
		КонецЕсли;
		
		КонтрольнаяСуммаПереданнойДопИнформации = "";
		Если ОписаниеПроблемы.Свойство("ДополнительнаяИнформация") Тогда
			КонтрольнаяСуммаПереданнойДопИнформации = ОбщегоНазначения.КонтрольнаяСуммаСтрокой(ОписаниеПроблемы.ДополнительнаяИнформация);
		КонецЕсли;
		
		Выборка = Результат.Выбрать();
		Пока Выборка.Следующий() Цикл
			
			Если ЗначениеЗаполнено(КонтрольнаяСуммаПереданнойДопИнформации) Тогда
				
				КонтрольнаяСуммаНайденнойДопИнформации  = ОбщегоНазначения.КонтрольнаяСуммаСтрокой(Выборка.ДополнительнаяИнформация);
				Если КонтрольнаяСуммаНайденнойДопИнформации <> КонтрольнаяСуммаПереданнойДопИнформации Тогда
					Продолжить;
				КонецЕсли;
				
			КонецЕсли;
			
			Если Выборка.ИгнорироватьПроблему <> Значение Тогда
				
				НаборЗаписей = РегистрыСведений.РезультатыПроверкиУчета.СоздатьНаборЗаписей();
				НаборЗаписей.Отбор.ПроблемныйОбъект.Установить(Выборка.ПроблемныйОбъект);
				НаборЗаписей.Отбор.ПравилоПроверки.Установить(Выборка.ПравилоПроверки);
				НаборЗаписей.Отбор.ВидПроверки.Установить(Выборка.ВидПроверки);
				НаборЗаписей.Прочитать();
				
				Запись = НаборЗаписей.Получить(0);
				Запись.ИгнорироватьПроблему = Значение;
				НаборЗаписей.Записать();
				
			КонецЕсли;
			
		КонецЦикла;
		
		ЗафиксироватьТранзакцию();
	Исключение
		ОтменитьТранзакцию();
		ВызватьИсключение;
	КонецПопытки;
	
КонецПроцедуры

// Возвращает параметры проверки по переданному идентификатору регламентного задания.
//
// Параметры:
//  ИдентификаторРегламентногоЗадания - Строка - поле для связки с текущим фоновым заданием.
//
// Возвращаемое значение:
//   Структура - со свойствами, Неопределено:
//       * Идентификатор - Строка - строковый идентификатор проверки.
//
Функция ПараметрыПроверкиПоИдентификаторуРегламентногоЗадания(ИдентификаторРегламентногоЗадания)
	
	Запрос = Новый Запрос(
	"ВЫБРАТЬ ПЕРВЫЕ 1
	|	ПравилаПроверкиУчета.Идентификатор КАК Идентификатор
	|ИЗ
	|	Справочник.ПравилаПроверкиУчета КАК ПравилаПроверкиУчета
	|ГДЕ
	|	ПравилаПроверкиУчета.ИдентификаторРегламентногоЗадания = &ИдентификаторРегламентногоЗадания
	|	И ПравилаПроверкиУчета.Использование");
	
	Запрос.УстановитьПараметр("ИдентификаторРегламентногоЗадания", Строка(ИдентификаторРегламентногоЗадания));
	Результат = Запрос.Выполнить().Выбрать();
	
	Если Не Результат.Следующий() Тогда
		Возврат Неопределено;
	Иначе
		
		СтруктураВозврата = Новый Структура;
		СтруктураВозврата.Вставить("Идентификатор", Результат.Идентификатор);
		
		Возврат СтруктураВозврата;
		
	КонецЕсли;
	
КонецФункции

// Возвращает текст и важность проблемы ведения учета.
//
Функция СведенияОПроблемеСОбъектом(СсылкаНаОбъект) Экспорт
	Запрос = Новый Запрос;
	Запрос.УстановитьПараметр("ПроблемныйОбъект", СсылкаНаОбъект);
	Запрос.Текст =
		"ВЫБРАТЬ РАЗРЕШЕННЫЕ
		|	РезультатыПроверкиУчета.УточнениеПроблемы КАК ТекстПроблемы,
		|	РезультатыПроверкиУчета.ВажностьПроблемы КАК ВажностьПроблемы
		|ИЗ
		|	РегистрСведений.РезультатыПроверкиУчета КАК РезультатыПроверкиУчета
		|ГДЕ
		|	РезультатыПроверкиУчета.ПроблемныйОбъект = &ПроблемныйОбъект";
	Результат = Запрос.Выполнить().Выгрузить();
	
	Сведения = Новый Структура;
	Сведения.Вставить("ТекстПроблемы", "");
	Сведения.Вставить("ВажностьПроблемы", Неопределено);
	
	Если Результат.Количество() = 0 Тогда
		Возврат Сведения;
	КонецЕсли;
	ЗаполнитьЗначенияСвойств(Сведения, Результат[0]);
	
	Возврат Сведения;
КонецФункции

#Область ОбновлениеСправочникаПроверок

Функция ЕстьИзмененияПараметровПроверокУчета() Экспорт
	
	УстановитьПривилегированныйРежим(Истина);
	ПоследниеИзменения = СтандартныеПодсистемыСервер.ИзмененияПараметраРаботыПрограммы(
		"СтандартныеПодсистемы.КонтрольВеденияУчета.СистемныеПроверки");
	Возврат ПоследниеИзменения = Неопределено Или ПоследниеИзменения.Количество() > 0;
	
КонецФункции

Процедура ДобавитьГруппыПроверок(ГруппыПроверок)
	
	СведенияОПроверках = СведенияОПроверкахВеденияУчета(ГруппыПроверок);
	
	Для Каждого ГруппаПроверок Из СведенияОПроверках Цикл
		
		ГруппаПроверокПоИдентификатору = ГруппаПроверок.Проверка;
		
		Если Не ЗначениеЗаполнено(ГруппаПроверокПоИдентификатору) Тогда
			ГруппаПроверокОбъект = Справочники.ПравилаПроверкиУчета.СоздатьГруппу();
		Иначе
			
			Если ГруппаПроверок.ПроверкаВеденияУчетаИзменена Тогда
				Продолжить;
			КонецЕсли;
			
			ГруппаПроверокОбъект = ГруппаПроверокПоИдентификатору.ПолучитьОбъект();
			Если ГруппаПроверокПоИдентификатору.ПометкаУдаления Тогда
				ГруппаПроверокОбъект.УстановитьПометкуУдаления(Ложь);
			КонецЕсли;
			
		КонецЕсли;
		
		ЗаполнитьЗначенияСвойств(ГруппаПроверокОбъект, ГруппаПроверок);
		
		РодительГруппыПроверки        = ГруппаПроверок.РодительПроверки;
		ГруппаПроверокОбъект.Родитель = РодительГруппыПроверки;
		
		Если ЗначениеЗаполнено(РодительГруппыПроверки) Тогда
			ГруппаПроверокОбъект.КонтекстПроверокВеденияУчета = ГруппаПроверок.КонтекстПроверокВеденияУчетаРодителя;
		Иначе
			ГруппаПроверокОбъект.КонтекстПроверокВеденияУчета = ГруппаПроверок.КонтекстПроверокВеденияУчета;
		КонецЕсли;
		
		// АПК:1327-выкл блокировка устанавливается выше по стеку.
		ОбновлениеИнформационнойБазы.ЗаписатьДанные(ГруппаПроверокОбъект);
		// АПК:1327-вкл
	КонецЦикла;
	
	Запрос = Новый Запрос(
	"ВЫБРАТЬ
	|	ГруппыПроверок.Идентификатор КАК Идентификатор,
	|	ГруппыПроверок.Наименование КАК Наименование
	|ПОМЕСТИТЬ ВТ_ГруппыПроверок
	|ИЗ
	|	&ГруппыПроверок КАК ГруппыПроверок
	|;
	|
	|////////////////////////////////////////////////////////////////////////////////
	|ВЫБРАТЬ
	|	ПравилаПроверкиУчета.Ссылка КАК Ссылка
	|ИЗ
	|	Справочник.ПравилаПроверкиУчета КАК ПравилаПроверкиУчета
	|		ЛЕВОЕ СОЕДИНЕНИЕ ВТ_ГруппыПроверок КАК ВТ_ГруппыПроверок
	|		ПО ПравилаПроверкиУчета.Идентификатор = ВТ_ГруппыПроверок.Идентификатор
	|ГДЕ
	|	ВТ_ГруппыПроверок.Наименование ЕСТЬ NULL
	|	И ПравилаПроверкиУчета.ЭтоГруппа
	|	И НЕ ПравилаПроверкиУчета.Предопределенный
	|	И НЕ ПравилаПроверкиУчета.ПроверкаВеденияУчетаИзменена");
	
	Запрос.МенеджерВременныхТаблиц = Новый МенеджерВременныхТаблиц;
	Запрос.УстановитьПараметр("ГруппыПроверок", ГруппыПроверок);
	
	// АПК:1327-выкл блокировка устанавливается выше по стеку.
	Результат = Запрос.Выполнить().Выбрать();
	// АПК:1327-вкл
	Пока Результат.Следующий() Цикл
		ГруппаПроверокОбъект = Результат.Ссылка.ПолучитьОбъект();
		ГруппаПроверокОбъект.УстановитьПометкуУдаления(Истина);
	КонецЦикла;
	
	Запрос.МенеджерВременныхТаблиц.Закрыть();
	
КонецПроцедуры

Процедура ДобавитьПроверки(Проверки)
	
	СведенияОПроверках = СведенияОПроверкахВеденияУчета(Проверки);
	
	Для Каждого СведенияОПроверке Из СведенияОПроверках Цикл
		
		Если Не ЗначениеЗаполнено(СведенияОПроверке.Проверка) Тогда
			
			ПроверкаОбъект = Справочники.ПравилаПроверкиУчета.СоздатьЭлемент();
			ПроверкаОбъект.СпособВыполнения = Перечисления.СпособыВыполненияПроверки.ПоОбщемуРасписанию;
			ПроверкаОбъект.ВажностьПроблемы = Перечисления.ВажностьПроблемыУчета.Ошибка;
			
		Иначе
			
			Если СведенияОПроверке.ПроверкаВеденияУчетаИзменена Тогда
				Продолжить;
			КонецЕсли;
			
			ПроверкаОбъект = СведенияОПроверке.Проверка.ПолучитьОбъект();
			Если ПроверкаОбъект.ПометкаУдаления Тогда
				ПроверкаОбъект.УстановитьПометкуУдаления(Ложь);
			КонецЕсли;
			
		КонецЕсли;
		
		ЗаполнитьЗначенияСвойств(ПроверкаОбъект, СведенияОПроверке);
		
		РодительПроверки        = СведенияОПроверке.РодительПроверки;
		ПроверкаОбъект.Родитель = РодительПроверки;
		
		Если ЗначениеЗаполнено(РодительПроверки) Тогда
			ПроверкаОбъект.КонтекстПроверокВеденияУчета = СведенияОПроверке.КонтекстПроверокВеденияУчетаРодителя;
		Иначе
			ПроверкаОбъект.КонтекстПроверокВеденияУчета = СведенияОПроверке.КонтекстПроверокВеденияУчета;
		КонецЕсли;
		
		ПроверкаОбъект.Использование = Не СведенияОПроверке.Отключена;
		
		Если ЗначениеЗаполнено(СведенияОПроверке.ЛимитПроблем) Тогда
			ПроверкаОбъект.ЛимитПроблем = СведенияОПроверке.ЛимитПроблем;
		Иначе
			ПроверкаОбъект.ЛимитПроблем = 1000;
		КонецЕсли;
		
		Если ЗначениеЗаполнено(СведенияОПроверке.ДатаНачалаПроверки) Тогда
			ПроверкаОбъект.ДатаНачалаПроверки = СведенияОПроверке.ДатаНачалаПроверки;
		КонецЕсли;
		
		// АПК:1327-выкл блокировка устанавливается выше по стеку.
		ОбновлениеИнформационнойБазы.ЗаписатьДанные(ПроверкаОбъект);
		// АПК:1327-вкл
	КонецЦикла;
	
	Запрос = Новый Запрос(
	"ВЫБРАТЬ
	|	Проверки.Идентификатор КАК Идентификатор,
	|	Проверки.Наименование КАК Наименование
	|ПОМЕСТИТЬ ВТ_Проверки
	|ИЗ
	|	&Проверки КАК Проверки
	|;
	|
	|////////////////////////////////////////////////////////////////////////////////
	|ВЫБРАТЬ
	|	ПравилаПроверкиУчета.Ссылка КАК Ссылка
	|ИЗ
	|	Справочник.ПравилаПроверкиУчета КАК ПравилаПроверкиУчета
	|		ЛЕВОЕ СОЕДИНЕНИЕ ВТ_Проверки КАК ВТ_Проверки
	|		ПО ПравилаПроверкиУчета.Идентификатор = ВТ_Проверки.Идентификатор
	|ГДЕ
	|	ВТ_Проверки.Наименование ЕСТЬ NULL
	|	И НЕ ПравилаПроверкиУчета.ЭтоГруппа
	|	И НЕ ПравилаПроверкиУчета.Предопределенный
	|	И НЕ ПравилаПроверкиУчета.ПроверкаВеденияУчетаИзменена");
	
	Запрос.МенеджерВременныхТаблиц = Новый МенеджерВременныхТаблиц;
	Запрос.УстановитьПараметр("Проверки", Проверки);
	
	// АПК:1327-выкл блокировка устанавливается выше по стеку.
	Результат = Запрос.Выполнить().Выбрать();
	// АПК:1327-вкл
	Пока Результат.Следующий() Цикл
		ПроверкаОбъект = Результат.Ссылка.ПолучитьОбъект();
		ПроверкаОбъект.УстановитьПометкуУдаления(Истина);
	КонецЦикла;
	
	Запрос.МенеджерВременныхТаблиц.Закрыть();
	
КонецПроцедуры

Процедура ОбновитьВспомогательныеДанныеСправочникаПоИзменениямКонфигурации()
	
	УстановитьПривилегированныйРежим(Истина);
	ПроверкиВеденияУчета = КонтрольВеденияУчетаСлужебныйПовтИсп.ПроверкиВеденияУчета();
	ПроверкаУникальностиЗаданныхЭлементов(ПроверкиВеденияУчета.ГруппыПроверок, ПроверкиВеденияУчета.Проверки);
	ДобавитьГруппыПроверок(ПроверкиВеденияУчета.ГруппыПроверок);
	ДобавитьПроверки(ПроверкиВеденияУчета.Проверки);
	
КонецПроцедуры

Процедура ПроверкаУникальностиЗаданныхЭлементов(ГруппыПроверок, Проверки)
	
	Запрос = Новый Запрос(
	"ВЫБРАТЬ
	|	Наименование КАК Наименование,
	|	Идентификатор КАК Идентификатор
	|ПОМЕСТИТЬ ВТ_ГруппыПроверок
	|ИЗ
	|	&ГруппыПроверок КАК ГруппыПроверок
	|;
	|
	|////////////////////////////////////////////////////////////////////////////////
	|ВЫБРАТЬ
	|	Наименование КАК Наименование,
	|	Идентификатор КАК Идентификатор
	|ПОМЕСТИТЬ ВТ_Проверки
	|ИЗ
	|	&Проверки КАК Проверки
	|;
	|
	|////////////////////////////////////////////////////////////////////////////////
	|ВЫБРАТЬ
	|	ВТ_ГруппыПроверок.Наименование КАК Наименование,
	|	ВТ_ГруппыПроверок.Идентификатор КАК Идентификатор
	|ПОМЕСТИТЬ ВТ_ОбщаяТаблица
	|ИЗ
	|	ВТ_ГруппыПроверок КАК ВТ_ГруппыПроверок
	|
	|ОБЪЕДИНИТЬ ВСЕ
	|
	|ВЫБРАТЬ
	|	ВТ_Проверки.Наименование,
	|	ВТ_Проверки.Идентификатор
	|ИЗ
	|	ВТ_Проверки КАК ВТ_Проверки
	|;
	|
	|////////////////////////////////////////////////////////////////////////////////
	|ВЫБРАТЬ
	|	ВТ_ОбщаяТаблица.Идентификатор КАК Идентификатор
	|ПОМЕСТИТЬ ВТ_ГруппировкаПоИдентификатору
	|ИЗ
	|	ВТ_ОбщаяТаблица КАК ВТ_ОбщаяТаблица
	|
	|СГРУППИРОВАТЬ ПО
	|	ВТ_ОбщаяТаблица.Идентификатор
	|
	|ИМЕЮЩИЕ
	|	КОЛИЧЕСТВО(ВТ_ОбщаяТаблица.Идентификатор) > 1
	|;
	|
	|////////////////////////////////////////////////////////////////////////////////
	|ВЫБРАТЬ
	|	ВТ_ОбщаяТаблица.Наименование КАК Наименование,
	|	ВТ_ОбщаяТаблица.Идентификатор КАК Идентификатор
	|ИЗ
	|	ВТ_ГруппировкаПоИдентификатору КАК ВТ_ГруппировкаПоИдентификатору
	|		ВНУТРЕННЕЕ СОЕДИНЕНИЕ ВТ_ОбщаяТаблица КАК ВТ_ОбщаяТаблица
	|		ПО ВТ_ГруппировкаПоИдентификатору.Идентификатор = ВТ_ОбщаяТаблица.Идентификатор
	|
	|УПОРЯДОЧИТЬ ПО
	|	Идентификатор
	|ИТОГИ ПО
	|	Идентификатор");
	
	Запрос.МенеджерВременныхТаблиц = Новый МенеджерВременныхТаблиц;
	Запрос.УстановитьПараметр("ГруппыПроверок", ГруппыПроверок);
	Запрос.УстановитьПараметр("Проверки",       Проверки);
	
	ТекстИсключения = "";
	Результат       = Запрос.Выполнить().Выбрать(ОбходРезультатаЗапроса.ПоГруппировкам);
	
	Пока Результат.Следующий() Цикл
		
		ТекстИсключения = ТекстИсключения + ?(ЗначениеЗаполнено(ТекстИсключения), Символы.ПС, "")
			+ СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(НСтр("ru = 'Дублирующийся идентификатор: ""%1""'"), Результат.Идентификатор);
			
		ДетальныйРезультат = Результат.Выбрать();
		Пока ДетальныйРезультат.Следующий() Цикл
			ТекстИсключения = ТекстИсключения + Символы.ПС + "- " + ДетальныйРезультат.Наименование;
		КонецЦикла;
		
	КонецЦикла;
	
	Запрос.МенеджерВременныхТаблиц.Закрыть();
	
	Если ЗначениеЗаполнено(ТекстИсключения) Тогда
		ВызватьИсключение СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(НСтр("ru = 'В процедуре %1 у следующих проверок совпадают идентификаторы:
		|%2'"), "КонтрольВеденияУчетаПереопределяемый.ПриОпределенииПроверок", ТекстИсключения);
	КонецЕсли;
	
КонецПроцедуры

Функция КонтрольнаяСуммаПроверок()
	
	ДанныеПроверок = Новый Соответствие;
	
	ПроверкиВеденияУчета = КонтрольВеденияУчетаСлужебныйПовтИсп.ПроверкиВеденияУчета();
	СоставПроверок = Новый Массив;
	
	Для Каждого ЭлементПроверокУчета Из ПроверкиВеденияУчета Цикл
		
		ЗначениеЭлементаПроверок = ЭлементПроверокУчета.Значение; // ТаблицаЗначений
		КолонкиЭлементаПроверок  = ЗначениеЭлементаПроверок.Колонки;
		
		Для Каждого СтрокаЭлементаПроверок Из ЗначениеЭлементаПроверок Цикл
			
			СвойстваПроверки = Новый Структура;
			Для Каждого КолонкаЭлементаПроверок Из КолонкиЭлементаПроверок Цикл
				СвойстваПроверки.Вставить(КолонкаЭлементаПроверок.Имя, СтрокаЭлементаПроверок[КолонкаЭлементаПроверок.Имя]);
			КонецЦикла;
			Если СвойстваПроверки.Количество() > 0 Тогда
				СоставПроверок.Добавить(СвойстваПроверки);
			КонецЕсли;
			
		КонецЦикла;
		
	КонецЦикла;
	
	ДанныеПроверок.Вставить(ОбщегоНазначения.КонтрольнаяСуммаСтрокой(Новый ФиксированныйМассив(СоставПроверок)));
	Возврат Новый ФиксированноеСоответствие(ДанныеПроверок);
	
КонецФункции

#КонецОбласти

#Область ПоставляемыеПроверки

// Исполняет проверку ссылочной целостности.
//
Процедура ПроверитьСсылочнуюЦелостность(Проверка, ПараметрыПроверки) Экспорт
	
	ПроверенныеСсылки = Новый Соответствие;
	
	ПараметрыВыполненияПроверки = ПрочитанныеПараметры(ПараметрыПроверки.ПараметрыВыполненияПроверки);
	Если ПараметрыВыполненияПроверки <> Неопределено Тогда
		Если ПараметрыВыполненияПроверки.ОбластьПроверки = "Регистры" Тогда
			НайтиБитыеСсылкиВРегистрах(ПараметрыВыполненияПроверки.ОбъектМетаданных, ПараметрыПроверки, ПроверенныеСсылки);
		Иначе
			НайтиБитыеСсылки(ПараметрыВыполненияПроверки.ОбъектМетаданных, ПараметрыПроверки, ПроверенныеСсылки);
		КонецЕсли;
		
		Возврат;
	КонецЕсли;
	
	Если ПараметрыПроверки.Свойство("ПроверяемыеОбъекты") И ПараметрыПроверки.ПроверяемыеОбъекты <> Неопределено Тогда
		Если ТипЗнч(ПараметрыПроверки.ПроверяемыеОбъекты) = Тип("Массив") Тогда
			ОбъектМетаданных = ПараметрыПроверки.ПроверяемыеОбъекты[0].Метаданные();
		Иначе
			ОбъектМетаданных = ПараметрыПроверки.ПроверяемыеОбъекты.Метаданные();
		КонецЕсли;
		НайтиБитыеСсылки(ОбъектМетаданных, ПараметрыПроверки, ПроверенныеСсылки);
		Возврат;
	КонецЕсли;
	
	ИсключаемыеИзПроверкиОбъекты = КонтрольВеденияУчетаСлужебныйПовтИсп.ИсключаемыеИзПроверкиОбъекты();
	
	Для Каждого ВидМетаданных Из СсылочныеВидыОбъектовМетаданных() Цикл
		Для Каждого ОбъектМетаданных Из ВидМетаданных Цикл
			ПолноеИмя = ОбъектМетаданных.ПолноеИмя();
			Если ЭтоНеразделенныйОбъектМетаданных(ПолноеИмя) Тогда
				Продолжить;
			КонецЕсли;
			Если ИсключаемыеИзПроверкиОбъекты.Найти(ПолноеИмя) <> Неопределено Тогда
				Продолжить;
			КонецЕсли;
			НайтиБитыеСсылки(ОбъектМетаданных, ПараметрыПроверки, ПроверенныеСсылки); // @skip-check query-in-loop - порционная проверка ведения учета
		КонецЦикла;
	КонецЦикла;
	
	Для Каждого ВидМетаданных Из РегистрыКакОбъектыМетаданных() Цикл
		Для Каждого ОбъектМетаданных Из ВидМетаданных Цикл
			ПолноеИмя = ОбъектМетаданных.ПолноеИмя();
			Если ЭтоНеразделенныйОбъектМетаданных(ПолноеИмя) Тогда
				Продолжить;
			КонецЕсли;
			Если ИсключаемыеИзПроверкиОбъекты.Найти(ПолноеИмя) <> Неопределено Тогда
				Продолжить;
			КонецЕсли;
			НайтиБитыеСсылкиВРегистрах(ОбъектМетаданных, ПараметрыПроверки, ПроверенныеСсылки); // @skip-check query-in-loop - порционная проверка ведения учета
		КонецЦикла;
	КонецЦикла;
	
КонецПроцедуры

// Исполняет проверку заполненности обязательных реквизитов.
//
Процедура ПроверитьНезаполненныеОбязательныеРеквизиты(Проверка, ПараметрыПроверки) Экспорт
	
	ПараметрыВыполненияПроверки = ПрочитанныеПараметры(ПараметрыПроверки.ПараметрыВыполненияПроверки);
	Если ПараметрыВыполненияПроверки <> Неопределено Тогда
		Если ПараметрыВыполненияПроверки.ОбластьПроверки = "Регистры" Тогда
			
			ОбъектМетаданных = ПараметрыВыполненияПроверки.ОбъектМетаданных; // ОбъектМетаданныхРегистрНакопления, ОбъектМетаданныхРегистрСведений, ОбъектМетаданныхРегистрРасчета, ОбъектМетаданныхРегистрБухгалтерии 
			НайтиНезаполненныеОбязательныеРеквизитыВРегистрах(ОбъектМетаданных, ПараметрыПроверки);
		Иначе
			НайтиНезаполненныеОбязательныеРеквизиты(ПараметрыВыполненияПроверки.ОбъектМетаданных, ПараметрыПроверки);
		КонецЕсли;
		
		Возврат;
	КонецЕсли;
	
	Если ПараметрыПроверки.Свойство("ПроверяемыеОбъекты") И ПараметрыПроверки.ПроверяемыеОбъекты <> Неопределено Тогда
		Если ТипЗнч(ПараметрыПроверки.ПроверяемыеОбъекты) = Тип("Массив") Тогда
			ОбъектМетаданных = ПараметрыПроверки.ПроверяемыеОбъекты[0].Метаданные();
		Иначе
			ОбъектМетаданных = ПараметрыПроверки.ПроверяемыеОбъекты.Метаданные();
		КонецЕсли;
		НайтиНезаполненныеОбязательныеРеквизиты(ОбъектМетаданных, ПараметрыПроверки);
		Возврат;
	КонецЕсли;
	
	ИсключаемыеИзПроверкиОбъекты = КонтрольВеденияУчетаСлужебныйПовтИсп.ИсключаемыеИзПроверкиОбъекты();
	
	Для Каждого ВидМетаданных Из СсылочныеВидыОбъектовМетаданных() Цикл
		
		Для Каждого ОбъектМетаданных Из ВидМетаданных Цикл
			ПолноеИмя = ОбъектМетаданных.ПолноеИмя();
			Если ЭтоНеразделенныйОбъектМетаданных(ПолноеИмя) Тогда
				Продолжить;
			КонецЕсли;
			Если Не ОбщегоНазначения.ОбъектМетаданныхДоступенПоФункциональнымОпциям(ОбъектМетаданных) Тогда
				Продолжить;
			КонецЕсли;
			Если ИсключаемыеИзПроверкиОбъекты.Найти(ПолноеИмя) <> Неопределено
				 Или СтрНачинаетсяС(ОбъектМетаданных.Имя, "Удалить") Тогда
					Продолжить;
			КонецЕсли;
			НайтиНезаполненныеОбязательныеРеквизиты(ОбъектМетаданных, ПараметрыПроверки); // @skip-check query-in-loop - порционная проверка ведения учета
		КонецЦикла;
		
	КонецЦикла;
	
	Для Каждого ВидМетаданных Из РегистрыКакОбъектыМетаданных() Цикл
		Для Каждого ОбъектМетаданных Из ВидМетаданных Цикл
			ПолноеИмя = ОбъектМетаданных.ПолноеИмя();
			Если ЭтоНеразделенныйОбъектМетаданных(ПолноеИмя) Тогда
				Продолжить;
			КонецЕсли;
			Если Не ОбщегоНазначения.ОбъектМетаданныхДоступенПоФункциональнымОпциям(ОбъектМетаданных) Тогда
				Продолжить;
			КонецЕсли;
			Если ИсключаемыеИзПроверкиОбъекты.Найти(ПолноеИмя) <> Неопределено
				 Или СтрНачинаетсяС(ОбъектМетаданных.Имя, "Удалить") Тогда
					Продолжить;
			КонецЕсли;
			НайтиНезаполненныеОбязательныеРеквизитыВРегистрах(ОбъектМетаданных, ПараметрыПроверки); // @skip-check query-in-loop - порционная проверка ведения учета
		КонецЦикла;
	КонецЦикла;
	
КонецПроцедуры

// Исполняет проверку наличия циклических ссылок.
//
Процедура ПроверитьЦиклическиеСсылки(Проверка, ПараметрыПроверки) Экспорт
	
	Для Каждого ВидМетаданных Из СсылочныеВидыОбъектовМетаданных() Цикл
		Для Каждого ОбъектМетаданных Из ВидМетаданных Цикл
			Если ЭтоНеразделенныйОбъектМетаданных(ОбъектМетаданных.ПолноеИмя()) Тогда
				Продолжить;
			КонецЕсли;
			Если Не ЕстьИерархия(ОбъектМетаданных.СтандартныеРеквизиты) Тогда
				Продолжить;
			КонецЕсли;
			НайтиЦиклическиеСсылки(ОбъектМетаданных, ПараметрыПроверки); // @skip-check query-in-loop - порционная проверка ведения учета
		КонецЦикла;
	КонецЦикла;
	
КонецПроцедуры

Процедура ИсправитьПроблемуЦиклическихСсылокВФоновомЗадании(Знач ПараметрыПроверки, АдресХранилища = Неопределено) Экспорт
	
	Проверка = ПроверкаПоИдентификатору(ПараметрыПроверки.ИдентификаторПроверки);
	Если Не ЗначениеЗаполнено(Проверка) Тогда
		Возврат;
	КонецЕсли;
	
	ИсправитьПроблемуЦиклическихСсылок(Проверка);
	
КонецПроцедуры

// Исполняет проверку наличия отсутствующих предопределенных элементов.
//
Процедура ПроверитьОтсутствующиеПредопределенныеЭлементы(Проверка, ПараметрыПроверки) Экспорт
	
	// Сброс кэша перед вызовом функции ОбщегоНазначенияКлиентСервер.ПредопределенныйЭлемент.
	ОбновитьПовторноИспользуемыеЗначения();
	
	ВидыОбъектовМетаданных = Новый Массив;
	ВидыОбъектовМетаданных.Добавить(Метаданные.Справочники);
	ВидыОбъектовМетаданных.Добавить(Метаданные.ПланыВидовХарактеристик);
	ВидыОбъектовМетаданных.Добавить(Метаданные.ПланыСчетов);
	ВидыОбъектовМетаданных.Добавить(Метаданные.ПланыВидовРасчета);
	
	Для Каждого ВидМетаданных Из ВидыОбъектовМетаданных Цикл
		Для Каждого ОбъектМетаданных Из ВидМетаданных Цикл
			Если ЭтоНеразделенныйОбъектМетаданных(ОбъектМетаданных.ПолноеИмя()) Тогда
				Продолжить;
			КонецЕсли;
			Если ОбъектМетаданных.ОбновлениеПредопределенныхДанных = Метаданные.СвойстваОбъектов.ОбновлениеПредопределенныхДанных.НеОбновлятьАвтоматически Тогда
				Продолжить;
			КонецЕсли;
			Если СтрНачинаетсяС(ОбъектМетаданных.Имя, "Удалить") Тогда
				Продолжить;
			КонецЕсли;
			НайтиОтсутствующиеПредопределенныеЭлементы(ОбъектМетаданных, ПараметрыПроверки); // @skip-check query-in-loop - порционная проверка ведения учета
		КонецЦикла;
	КонецЦикла;
	
КонецПроцедуры

// Исполняет проверку наличия дублирующихся предопределенных элементов.
//
Процедура ПроверитьДублированиеПредопределенныхЭлементов(Проверка, ПараметрыПроверки) Экспорт
	
	ВидыОбъектовМетаданных = Новый Массив;
	ВидыОбъектовМетаданных.Добавить(Метаданные.Справочники);
	ВидыОбъектовМетаданных.Добавить(Метаданные.ПланыВидовХарактеристик);
	ВидыОбъектовМетаданных.Добавить(Метаданные.ПланыСчетов);
	ВидыОбъектовМетаданных.Добавить(Метаданные.ПланыВидовРасчета);
	
	Для Каждого ВидМетаданных Из ВидыОбъектовМетаданных Цикл
		
		Если ВидМетаданных.Количество() = 0 Тогда
			Продолжить;
		КонецЕсли;
		
		НайтиДублиПредопределенныхЭлементов(ВидМетаданных, ПараметрыПроверки); // @skip-check query-in-loop - порционная проверка ведения учета
		
	КонецЦикла;
	
КонецПроцедуры

// Исполняет проверку отсутствующих предопределенных узлов плана обмена.
//
Процедура ПроверитьНаличиеПредопределенныхУзловПлановОбмена(Проверка, ПараметрыПроверки) Экспорт
	
	МетаданныеПланыОбмена = Метаданные.ПланыОбмена;
	Для Каждого МетаданныеПланОбмена Из МетаданныеПланыОбмена Цикл
		
		Если ЭтоНеразделенныйОбъектМетаданных(МетаданныеПланОбмена.ПолноеИмя()) Тогда
			Продолжить;
		КонецЕсли;
		Если ПланыОбмена[МетаданныеПланОбмена.Имя].ЭтотУзел() <> Неопределено Тогда
			Продолжить;
		КонецЕсли;
		
		Проблема = ОписаниеПроблемы(ОбщегоНазначения.ИдентификаторОбъектаМетаданных(МетаданныеПланОбмена.ПолноеИмя()), ПараметрыПроверки); // @skip-check query-in-loop - порционная проверка ведения учета
		Проблема.УточнениеПроблемы = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(НСтр("ru = 'В плане обмена ""%1"" отсутствует предопределенный узел (%2 = Неопределено).'"), МетаданныеПланОбмена.Имя, "ЭтотУзел()");
		ЗаписатьПроблему(Проблема, ПараметрыПроверки); // @skip-check query-in-loop - порционная проверка ведения учета
		
	КонецЦикла;
	
КонецПроцедуры

#КонецОбласти

#Область ПользовательскаяИндикацияОшибок

// Параметры:
//   Форма                - ФормаКлиентскогоПриложения
//   КлючУникальностиИмен - Строка
//   ИмяРодителяГруппы    - Строка
//                        - Неопределено
//   ВыводитьСнизу        - Булево
//
Функция РазместитьГруппуИндикатораОшибки(Форма, КлючУникальностиИмен, ИмяРодителяГруппы = Неопределено, ВыводитьСнизу = Ложь) Экспорт
	
	ВсеЭлементыФормы = Форма.Элементы;
	
	Если ИмяРодителяГруппы = Неопределено Тогда
		КонтекстРазмещения = Форма;
	Иначе
		РодительГруппы = ВсеЭлементыФормы.Найти(ИмяРодителяГруппы);
		Если РодительГруппы = Неопределено Тогда
			ВызватьИсключение СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(НСтр("ru = 'Не существует указанная группа формы: ""%1""'"), ИмяРодителяГруппы);
		КонецЕсли;
		КонтекстРазмещения = РодительГруппы;
	КонецЕсли;
	
	ГруппаИндикатораОшибки = ВсеЭлементыФормы.Добавить("ГруппаИндикатораОшибки_" + КлючУникальностиИмен, Тип("ГруппаФормы"), КонтекстРазмещения); // ТаблицаФормы
	ГруппаИндикатораОшибки.Вид                      = ВидГруппыФормы.ОбычнаяГруппа;
	ГруппаИндикатораОшибки.ОтображатьЗаголовок      = Ложь;
	ГруппаИндикатораОшибки.Группировка              = ГруппировкаПодчиненныхЭлементовФормы.ГоризонтальнаяВсегда;
	ГруппаИндикатораОшибки.РастягиватьПоГоризонтали = Истина;
	ГруппаИндикатораОшибки.ЦветФона                 = ЦветаСтиля.ФонУправляющегоПоля;
	
	ПодчиненныеЭлементыКонтекста = КонтекстРазмещения.ПодчиненныеЭлементы;
	Если ВыводитьСнизу Тогда
		ВсеЭлементыФормы.Переместить(ГруппаИндикатораОшибки, КонтекстРазмещения);
	Иначе
		Если ПодчиненныеЭлементыКонтекста.Количество() > 0 Тогда
			ВсеЭлементыФормы.Переместить(ГруппаИндикатораОшибки, КонтекстРазмещения, ПодчиненныеЭлементыКонтекста.Получить(0));
		КонецЕсли;
	КонецЕсли;
	
	Возврат ГруппаИндикатораОшибки;
	
КонецФункции

// Параметры:
//   Форма - ФормаКлиентскогоПриложения
//   ГруппаИндикатораОшибки       - ГруппаФормы
//   КлючУникальностиИмен         - Строка
//   КартинкаИндикатораПроблем    - Картинка
//                                - Неопределено
//   ОбщаяСтрокаИндикатор         - ФорматированнаяСтрока
//
Процедура ЗаполнитьГруппуИндикатораОшибки(Форма, ГруппаИндикатораОшибки, КлючУникальностиИмен,
	ОбщаяСтрокаИндикатор, Настройки) Экспорт
	
	ДатаПоследнейПроверки     = Настройки.ДатаПоследнейПроверки;
	Подсказка = НСтр("ru = '(По состоянию на %1)'");
	Подсказка = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(Подсказка, ДатаПоследнейПроверки);
	
	ЭлементыУправляемойФормы = Форма.Элементы;
	
	КартинкаИндикаторОшибки = ЭлементыУправляемойФормы.Добавить("ДекорацияКартинка_" + КлючУникальностиИмен, Тип("ДекорацияФормы"), ГруппаИндикатораОшибки);
	КартинкаИндикаторОшибки.Вид            = ВидДекорацииФормы.Картинка;
	КартинкаИндикаторОшибки.Картинка       = КартинкаПроблемы(Настройки);
	КартинкаИндикаторОшибки.РазмерКартинки = РазмерКартинки.РеальныйРазмер;
	
	ДекорацияНадпись = ЭлементыУправляемойФормы.Добавить("ДекорацияНадпись_" + КлючУникальностиИмен, Тип("ДекорацияФормы"), ГруппаИндикатораОшибки);
	ДекорацияНадпись.Вид                   = ВидДекорацииФормы.Надпись;
	ДекорацияНадпись.Заголовок             = ОбщаяСтрокаИндикатор;
	ДекорацияНадпись.ВертикальноеПоложение = ВертикальноеПоложениеЭлемента.Центр;
	ДекорацияНадпись.УстановитьДействие("ОбработкаНавигационнойСсылки", "Подключаемый_ОткрытьОтчетПоПроблемам");
	ДекорацияНадпись.ОтображениеПодсказки = ОтображениеПодсказки.ОтображатьСправа;
	ДекорацияНадпись.АвтоМаксимальнаяШирина = Ложь;
	ДекорацияНадпись.РасширеннаяПодсказка.Заголовок = Подсказка;
	ДекорацияНадпись.РасширеннаяПодсказка.Высота = 1;
	
КонецПроцедуры

// Формирует общую строку-индикатор наличия ошибок. Состоит из поясняющего текста и
// гиперссылки открывающей отчет по проблемам объекта.
//
// Параметры:
//  ФормаКлиентскогоПриложения   - ФормаКлиентскогоПриложения - форма объекта на которой расположена группа индикатора.
//  СсылкаНаОбъект               - ЛюбаяСсылка - ссылка на объект по которому найдены ошибки.
//  КоличествоПроблемПоОбъекту   - Число - найденное количество проблем по объекту.
//  ПояснениеИндикатораПроблем   - Строка, Неопределено - строка идентифицирующая  наличие проблем у текущего объекта.
//                                 Может быть переопределена конечным разработчиком - достаточно добавить параметр при вызове.
//  ГиперссылкаИндикатораПроблем - Строка, Неопределено - строка представляющая гиперссылку, при нажатии 
//                                 на которую будет открыт и сформирован отчет по проблемам текущего объекта.
//
Функция СформироватьОбщуюСтрокуИндикатор(Форма, СсылкаНаОбъект, Настройки) Экспорт
	
	ПояснениеИндикатораПроблем   = Настройки.ПояснениеИндикатораПроблем;
	ГиперссылкаИндикатораПроблем = Настройки.ГиперссылкаИндикатораПроблем;
	КоличествоПроблемПоОбъекту   = Настройки.КоличествоПроблем;
	ТекстоваяСсылка = "Основной";
	
	Если Настройки.ДетальныйВид И ЗначениеЗаполнено(Настройки.ТекстПроблемы) Тогда
		Возврат Настройки.ТекстПроблемы;
	КонецЕсли;
	
	Если ПояснениеИндикатораПроблем <> Неопределено Тогда
		ПоясняющаяНадпись = ПояснениеИндикатораПроблем
	Иначе
		ПоясняющаяНадпись = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(НСтр("ru = 'В этом %1'"), ПредставлениеОбъектаПоТипу(СсылкаНаОбъект));
	КонецЕсли;
	
	Если ГиперссылкаИндикатораПроблем <> Неопределено Тогда
		Гиперссылка = ГиперссылкаИндикатораПроблем;
	Иначе
		Гиперссылка = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(НСтр("ru = 'найдены проблемы (%1)'"), Формат(КоличествоПроблемПоОбъекту, "ЧГ=0"));
	КонецЕсли;
	
	Возврат Новый ФорматированнаяСтрока(ПоясняющаяНадпись + " ", Новый ФорматированнаяСтрока(Гиперссылка, , , , ТекстоваяСсылка));
	
КонецФункции

// См. КонтрольВеденияУчетаПереопределяемый.ПриОпределенииПараметровГруппыИндикации.
Процедура ПриОпределенииПараметровГруппыИндикации(ПараметрыГруппыИндикации, ТипСсылки) Экспорт
	
	ПараметрыГруппыИндикации.Вставить("ИмяРодителяГруппы", Неопределено);
	ПараметрыГруппыИндикации.Вставить("ВыводитьСнизу",     Ложь);
	ПараметрыГруппыИндикации.Вставить("ДетальныйВид",      Истина);
	
КонецПроцедуры

// См. КонтрольВеденияУчетаПереопределяемый.ПриОпределенииПараметровКолонкиИндикации.
Процедура ПриОпределенииПараметровКолонкиИндикации(ПараметрыКолонкиИндикации, ПолноеИмя) Экспорт
	
	ПараметрыКолонкиИндикации.Вставить("ПоложениеЗаголовка", ПоложениеЗаголовкаЭлементаФормы.Нет);
	ПараметрыКолонкиИндикации.Вставить("Ширина",             2);
	ПараметрыКолонкиИндикации.Вставить("ВыводитьПоследней",  Ложь);
	
КонецПроцедуры

#КонецОбласти

#Область ВыполнениеПроверок

// См. КонтрольВеденияУчета.ВыполнитьПроверку.
Процедура ВыполнитьПроверку(Проверка, ПараметрыВыполненияПроверки = Неопределено, ПроверяемыеОбъекты = Неопределено) Экспорт
	
	Если ТипЗнч(Проверка) = Тип("Строка") Тогда
		ВыполняемаяПроверка = КонтрольВеденияУчета.ПроверкаПоИдентификатору(Проверка);
	Иначе
		ВыполняемаяПроверка = Проверка;
	КонецЕсли;
	
	Если Не ОбновлениеИнформационнойБазы.ОбъектОбработан(ВыполняемаяПроверка).Обработан Тогда
		Возврат;
	КонецЕсли;
	
	ЗаданыПараметрыВыполненияПроверки = ПараметрыВыполненияПроверки <> Неопределено;
	ПараметрыПроверки = ПодготовитьПараметрыПроверки(ВыполняемаяПроверка, ПараметрыВыполненияПроверки);
	Если ПроверкаУжеВыполняется(ПараметрыПроверки.ИдентификаторРегламентногоЗадания) Тогда
		Возврат;
	КонецЕсли;
	
	Если ЗначениеЗаполнено(ПроверяемыеОбъекты) И Не ПараметрыПроверки.ПоддерживаетВыборочнуюПроверку Тогда
		Возврат;
	КонецЕсли;
	
	ПроверкиВеденияУчета = КонтрольВеденияУчетаСлужебныйПовтИсп.ПроверкиВеденияУчета();
	Проверки             = ПроверкиВеденияУчета.Проверки;
	СтрокаПроверки       = Проверки.Найти(ПараметрыПроверки.Идентификатор, "Идентификатор");
	Если СтрокаПроверки = Неопределено Тогда 
		Возврат;
	КонецЕсли;
		
	Если СтрокаПроверки.БезОбработчикаПроверки Тогда
		Возврат;
	КонецЕсли;
	
	ПараметрыПроверки.Вставить("ПроверяемыеОбъекты", ПроверяемыеОбъекты);
	
	РезультатПоследнейПроверки = Неопределено;
	Если ЗначениеЗаполнено(ПроверяемыеОбъекты) Тогда
		РезультатПоследнейПроверки = РезультатПоследнейПроверкиОбъекта(ПараметрыПроверки);
	ИначеЕсли Не ЗаданыПараметрыВыполненияПроверки Тогда
		ОчиститьРезультатыПередВыполнениемПроверки(ВыполняемаяПроверка);
	КонецЕсли;
	
	МодульОценкаПроизводительности = Неопределено;
	Если ОбщегоНазначения.ПодсистемаСуществует("СтандартныеПодсистемы.ОценкаПроизводительности") Тогда
		МодульОценкаПроизводительности = ОбщегоНазначения.ОбщийМодуль("ОценкаПроизводительности");
		ОписаниеЗамера = МодульОценкаПроизводительности.НачатьЗамерДлительнойОперации(ПараметрыПроверки.Идентификатор);
	КонецЕсли;
	
	УстановитьСостояниеПроверкиВеденияУчета(ВыполняемаяПроверка);
	
	ПараметрыСеанса.СчетчикПроблемВеденияУчета = 0;
	
	ПараметрыОбработчика = Новый Массив;
	ПараметрыОбработчика.Добавить(ВыполняемаяПроверка);
	ПараметрыОбработчика.Добавить(ПараметрыПроверки);
	ОбщегоНазначения.ВыполнитьМетодКонфигурации(СтрокаПроверки.ОбработчикПроверки, ПараметрыОбработчика);
	
	Если ПараметрыСеанса.СчетчикПроблемВеденияУчета > 0
		И ПараметрыПроверки.ВажностьПроблемы = Перечисления.ВажностьПроблемыУчета.Ошибка Тогда
		Комментарий = НСтр("ru = 'При выполнении проверки ""%1"" обнаружены ошибки (%2)'");
		Комментарий = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(Комментарий,
			ПараметрыПроверки.Наименование, ПараметрыСеанса.СчетчикПроблемВеденияУчета);
		ЗаписьЖурналаРегистрации(НСтр("ru = 'Контроль ведения учета'", ОбщегоНазначения.КодОсновногоЯзыка()),
			УровеньЖурналаРегистрации.Ошибка,
			,
			,
			Комментарий);
		
		ПараметрыСеанса.СчетчикПроблемВеденияУчета = 0;
	КонецЕсли;
	
	Если РезультатПоследнейПроверки <> Неопределено Тогда
		УдалитьРезультатыПредыдущейПроверки(РезультатПоследнейПроверки);
	КонецЕсли;
	
	Если МодульОценкаПроизводительности <> Неопределено Тогда
		МодульОценкаПроизводительности.ЗакончитьЗамерДлительнойОперации(ОписаниеЗамера, 0);
	КонецЕсли;
	
КонецПроцедуры

// Для вызова из процедуры КонтрольВеденияУчета.ПослеЗаписиНаСервере.
// Делает проверку переданных объектов.
//
Процедура ПроверитьОбъект(ПроверяемыеОбъекты, Проверки) Экспорт
	Для Каждого Проверка Из Проверки Цикл
		ВыполнитьПроверку(Проверка, , ПроверяемыеОбъекты); // @skip-check query-in-loop - порционная проверка ведения учета
	КонецЦикла;
КонецПроцедуры

// Обработчик регламентного задания "ПроверкаВеденияУчета". Предназначен для обработки
// фонового запуска проверок системы.
//
//   Параметры:
//       ИдентификаторРегламентногоЗадания - Строка
//                                         - Неопределено - строковый идентификатор регламентного задания.
//
Процедура ПроверитьВедениеУчета(ИдентификаторРегламентногоЗадания = Неопределено) Экспорт
	
	ОбщегоНазначения.ПриНачалеВыполненияРегламентногоЗадания(Метаданные.РегламентныеЗадания.ПроверкаВеденияУчета);
	
	Если ИдентификаторРегламентногоЗадания <> Неопределено Тогда
		
		ПараметрыПроверки = ПараметрыПроверкиПоИдентификаторуРегламентногоЗадания(ИдентификаторРегламентногоЗадания);
		Если ПараметрыПроверки <> Неопределено Тогда
			ВыполнитьПроверку(ПараметрыПроверки.Идентификатор);
		КонецЕсли;
		
	Иначе
		
		Запрос = Новый Запрос(
		"ВЫБРАТЬ
		|	ПравилаПроверкиУчета.Идентификатор КАК Идентификатор
		|ИЗ
		|	Справочник.ПравилаПроверкиУчета КАК ПравилаПроверкиУчета
		|ГДЕ
		|	ПравилаПроверкиУчета.СпособВыполнения = ЗНАЧЕНИЕ(Перечисление.СпособыВыполненияПроверки.ПоОбщемуРасписанию)
		|	И ПравилаПроверкиУчета.Использование");
		
		Результат = Запрос.Выполнить().Выбрать();
		Пока Результат.Следующий() Цикл
			ВыполнитьПроверку(Результат.Идентификатор); // @skip-check query-in-loop - порционная проверка ведения учета
		КонецЦикла;
		
	КонецЕсли;
	
КонецПроцедуры

Процедура ВыполнитьПроверки(Знач Проверки) Экспорт
	
	Для Каждого Проверка Из Проверки Цикл
		ВыполнитьПроверку(Проверка); // @skip-check query-in-loop - порционная проверка ведения учета
	КонецЦикла;
	
КонецПроцедуры

// См. КонтрольВеденияУчета.ПроверкаПоИдентификатору.
Функция ПроверкаПоИдентификатору(Идентификатор) Экспорт
	
	Запрос = Новый Запрос(
	"ВЫБРАТЬ ПЕРВЫЕ 1
	|	ПравилаПроверкиУчета.Ссылка КАК Проверка
	|ИЗ
	|	Справочник.ПравилаПроверкиУчета КАК ПравилаПроверкиУчета
	|ГДЕ
	|	ПравилаПроверкиУчета.Идентификатор = &Идентификатор
	|	И НЕ ПравилаПроверкиУчета.ПометкаУдаления");
	
	Запрос.УстановитьПараметр("Идентификатор", Идентификатор);
	Результат = Запрос.Выполнить().Выбрать();
	
	Если Не Результат.Следующий() Тогда
		Возврат Справочники.ПравилаПроверкиУчета.ПустаяСсылка();
	Иначе
		Возврат Результат.Проверка;
	КонецЕсли;
	
КонецФункции

Функция СведенияОПроверкахВеденияУчета(Проверки)
	
	ИменаКолонок = Новый Массив;
	Для Каждого Колонка Из Проверки.Колонки Цикл
		Если Колонка.Имя = "Идентификатор"
			Или Колонка.Имя = "ИдентификаторГруппы" Тогда
			Продолжить;
		КонецЕсли;
		ИменаКолонок.Добавить("Проверки" + "." + Колонка.Имя);
	КонецЦикла;
	
	Запрос = Новый Запрос(
	"ВЫБРАТЬ
	|	Проверки.Идентификатор КАК Идентификатор,
	|	Проверки.ИдентификаторГруппы КАК ИдентификаторГруппы,
	|	&ВыбираемыеКолонки
	|ПОМЕСТИТЬ ВТПроверки
	|ИЗ
	|	&Проверки КАК Проверки
	|;
	|
	|////////////////////////////////////////////////////////////////////////////////
	|ВЫБРАТЬ
	|	Проверки.Идентификатор КАК Идентификатор,
	|	Проверки.ИдентификаторГруппы КАК ИдентификаторГруппы,
	|	&ВыбираемыеКолонки,
	|	ЕСТЬNULL(ПроверкиУчета.Ссылка, ЗНАЧЕНИЕ(Справочник.ПравилаПроверкиУчета.ПустаяСсылка)) КАК Проверка,
	|	ПроверкиУчета.ПроверкаВеденияУчетаИзменена КАК ПроверкаВеденияУчетаИзменена,
	|	РодителиПроверокУчета.Ссылка КАК РодительПроверки,
	|	РодителиПроверокУчета.КонтекстПроверокВеденияУчета КАК КонтекстПроверокВеденияУчетаРодителя
	|ИЗ
	|	ВТПроверки КАК Проверки
	|		ЛЕВОЕ СОЕДИНЕНИЕ Справочник.ПравилаПроверкиУчета КАК ПроверкиУчета
	|		ПО Проверки.Идентификатор = ПроверкиУчета.Идентификатор
	|			И (НЕ ПроверкиУчета.ПометкаУдаления)
	|		ЛЕВОЕ СОЕДИНЕНИЕ Справочник.ПравилаПроверкиУчета КАК РодителиПроверокУчета
	|		ПО Проверки.ИдентификаторГруппы = РодителиПроверокУчета.Идентификатор
	|			И (НЕ РодителиПроверокУчета.ПометкаУдаления)");
	Запрос.Текст = СтрЗаменить(Запрос.Текст, "&ВыбираемыеКолонки", СтрСоединить(ИменаКолонок, ","));
	
	Запрос.УстановитьПараметр("Проверки", Проверки);
	Результат = Запрос.Выполнить().Выгрузить();
	
	Возврат Результат;
	
КонецФункции

// См. КонтрольВеденияУчета.ВыполнитьПроверкиВКонтексте.
Функция ПроверкиПоКонтексту(КонтекстПроверокВеденияУчета) Экспорт
	
	Запрос = Новый Запрос(
	"ВЫБРАТЬ
	|	""ВыборкаЭлементовБезРодителейСУчетомКонтекста"" КАК НазначениеЗапроса,
	|	ЗНАЧЕНИЕ(Справочник.ПравилаПроверкиУчета.ПустаяСсылка) КАК Родитель,
	|	ПравилаПроверкиУчета.Ссылка КАК Проверка
	|ИЗ
	|	Справочник.ПравилаПроверкиУчета КАК ПравилаПроверкиУчета
	|ГДЕ
	|	НЕ ПравилаПроверкиУчета.ЭтоГруппа
	|	И ПравилаПроверкиУчета.Использование
	|	И ПравилаПроверкиУчета.Родитель = ЗНАЧЕНИЕ(Справочник.ПравилаПроверкиУчета.ПустаяСсылка)
	|	И ПравилаПроверкиУчета.КонтекстПроверокВеденияУчета = &КонтекстПроверокВеденияУчета
	|
	|ОБЪЕДИНИТЬ ВСЕ
	|
	|ВЫБРАТЬ
	|	""ВыборкаГруппСУчетомКонтекста"",
	|	ПравилаПроверкиУчета.Родитель,
	|	ПравилаПроверкиУчета.Ссылка
	|ИЗ
	|	Справочник.ПравилаПроверкиУчета КАК ПравилаПроверкиУчета
	|ГДЕ
	|	ПравилаПроверкиУчета.ЭтоГруппа
	|	И ПравилаПроверкиУчета.КонтекстПроверокВеденияУчета = &КонтекстПроверокВеденияУчета
	|
	|ОБЪЕДИНИТЬ ВСЕ
	|
	|ВЫБРАТЬ
	|	""ВыборкаЭлементовСРодителямиБезУчетаКонтекста"",
	|	ПравилаПроверкиУчета.Родитель,
	|	ПравилаПроверкиУчета.Ссылка
	|ИЗ
	|	Справочник.ПравилаПроверкиУчета КАК ПравилаПроверкиУчета
	|ГДЕ
	|	НЕ ПравилаПроверкиУчета.ЭтоГруппа
	|	И ПравилаПроверкиУчета.Использование
	|	И ПравилаПроверкиУчета.Родитель <> ЗНАЧЕНИЕ(Справочник.ПравилаПроверкиУчета.ПустаяСсылка)");
	
	Запрос.УстановитьПараметр("КонтекстПроверокВеденияУчета", КонтекстПроверокВеденияУчета);
	ВсеПроверки = Запрос.Выполнить().Выгрузить();
	
	Результат  = Новый Массив;
	РодительскиеПроверки = ВсеПроверки.Скопировать(ВсеПроверки.НайтиСтроки(
		Новый Структура("НазначениеЗапроса", "ВыборкаГруппСУчетомКонтекста")), "Родитель, Проверка");
	
	Для Каждого СтрокаРезультата Из ВсеПроверки Цикл
		
		Если СтрокаРезультата.НазначениеЗапроса = "ВыборкаЭлементовБезРодителейСУчетомКонтекста"
			Или (СтрокаРезультата.НазначениеЗапроса = "ВыборкаЭлементовСРодителямиБезУчетаКонтекста"
			И РодительскиеПроверки.Найти(СтрокаРезультата.Родитель) <> Неопределено) Тогда
			Результат.Добавить(СтрокаРезультата.Проверка);
		КонецЕсли;
		
	КонецЦикла;
	
	Возврат ОбщегоНазначенияКлиентСервер.СвернутьМассив(Результат);
	
КонецФункции

// См. КонтрольВеденияУчета.ПараметрыВыполненияПроверки.
Функция ПараметрыВыполненияПроверки(Знач Свойство1, Знач Свойство2 = Неопределено, Знач Свойство3 = Неопределено,
	Знач Свойство4 = Неопределено, Знач Свойство5 = Неопределено, Знач ДополнительныеСвойства = Неопределено) Экспорт
	
	КоличествоСвойств             = КоличествоСвойств();
	НайденКрайнийЗначащийПараметр = Ложь;
	
	Для Индекс = 2 По КоличествоСвойств Цикл
		Если Индекс = 2 Тогда
			ЗначениеПараметра = Свойство2;
		ИначеЕсли Индекс = 3 Тогда
			ЗначениеПараметра = Свойство3;
		ИначеЕсли Индекс = 4 Тогда
			ЗначениеПараметра = Свойство4;
		ИначеЕсли Индекс = 5 Тогда
			ЗначениеПараметра = Свойство5;
		КонецЕсли;
		Если ЗначениеПараметра = Неопределено Тогда
			Если Не НайденКрайнийЗначащийПараметр Тогда
				НайденКрайнийЗначащийПараметр = Истина;
			КонецЕсли;
		Иначе
			Если НайденКрайнийЗначащийПараметр Тогда
				ТекстСообщения = НСтр("ru = 'Параметры выполнения проверки заданы не по порядку в %1.'");
				ТекстСообщения = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(ТекстСообщения, "КонтрольВеденияУчета.ПараметрыВыполненияПроверки");
				ВызватьИсключение ТекстСообщения;
			КонецЕсли;
		КонецЕсли;
	КонецЦикла;
	
	Если ДополнительныеСвойства <> Неопределено И Свойство5 = Неопределено Тогда
		ТекстСообщения = НСтр("ru = 'Параметры выполнения проверки заданы не по порядку в %1.'");
		ТекстСообщения = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(ТекстСообщения, "КонтрольВеденияУчета.ПараметрыВыполненияПроверки");
		ВызватьИсключение ТекстСообщения;
	КонецЕсли;
	
	ВсеПараметры = Новый Массив;
	ВсеПараметры.Добавить(Свойство1);
	Для Индекс = 2 По КоличествоСвойств Цикл
		
		Если Индекс = 2 Тогда
			ЗначениеСвойства = Свойство2;
		ИначеЕсли Индекс = 3 Тогда
			ЗначениеСвойства = Свойство3;
		ИначеЕсли Индекс = 4 Тогда
			ЗначениеСвойства = Свойство4;
		ИначеЕсли Индекс = 5 Тогда
			ЗначениеСвойства = Свойство5;
		КонецЕсли;
		Если ЗначениеСвойства = Неопределено Тогда
			Прервать;
		КонецЕсли;
		ВсеПараметры.Добавить(ЗначениеСвойства);
		
	КонецЦикла;
	
	Если ДополнительныеСвойства <> Неопределено Тогда
		ОбщегоНазначенияКлиентСервер.ДополнитьМассив(ВсеПараметры, ДополнительныеСвойства); 
	КонецЕсли;
	
	Возврат ПараметрыВыполненияПроверкиИзМассива(ВсеПараметры);
	
КонецФункции

Функция ПараметрыВыполненияПроверкиИзМассива(Знач Параметры)
	
	НаименованиеВидаПроверки = "";
	Индекс = 1;
	Результат = Новый Структура;
	
	Для Каждого ТекущийПараметр Из Параметры Цикл
		
		ОбщегоНазначенияКлиентСервер.ПроверитьПараметр("КонтрольВеденияУчета.ПараметрыВыполненияПроверки", 
			"Свойство" + Формат(Индекс, "ЧГ=0"), ТекущийПараметр, ОжидаемыеТипыСвойствВидовПроверок());
			
		НаименованиеВидаПроверки = НаименованиеВидаПроверки + ?(ЗначениеЗаполнено(НаименованиеВидаПроверки), ", ", "") 
			+ Формат(ТекущийПараметр, "ДЛФ=Д; ЧГ=0");
		Результат.Вставить("Свойство" + Формат(Параметры.Найти(ТекущийПараметр) + 1, "ЧГ=0"), ТекущийПараметр);
		
		Индекс = Индекс + 1;
		
	КонецЦикла;
	
	Результат.Вставить("Наименование", НаименованиеВидаПроверки);
	Возврат Результат;

КонецФункции

Процедура УстановитьСостояниеПроверкиВеденияУчета(Проверка)
	
	УстановитьПривилегированныйРежим(Истина);
	МенеджерЗаписи = РегистрыСведений.СостоянияПроверокВеденияУчета.СоздатьМенеджерЗаписи();
	МенеджерЗаписи.Проверка = Проверка;
	МенеджерЗаписи.ПоследнийЗапуск = ТекущаяДатаСеанса();
	МенеджерЗаписи.Записать();
	
КонецПроцедуры

#КонецОбласти

#Область КонтрольСсылочнойЦелостности

Процедура НайтиБитыеСсылки(ОбъектМетаданных, ПараметрыПроверки, ПроверенныеСсылки)
	
	СсылочныеРеквизиты = СсылочныеРеквизитыОбъекта(ОбъектМетаданных);
	Если СсылочныеРеквизиты.Количество() = 0 Тогда
		Возврат;
	КонецЕсли;
	
	Запрос = Новый Запрос;
	ЕстьОграничениеПоДате = ЗначениеЗаполнено(ПараметрыПроверки.ДатаНачалаПроверки) 
		И (ОбщегоНазначения.ЭтоДокумент(ОбъектМетаданных) Или ОбщегоНазначения.ЭтоЗадача(ОбъектМетаданных) 
			Или ОбщегоНазначения.ЭтоБизнесПроцесс(ОбъектМетаданных));
			
	Если ЕстьОграничениеПоДате Тогда
		ТекстЗапроса = 
		"ВЫБРАТЬ ПЕРВЫЕ 1000
		|	ПсевдонимЗаданнойТаблицы.Ссылка КАК ПроблемныйОбъект,
		|	&СсылочныеРеквизиты
		|	,&РеквизитыТабличныхЧастей
		|ИЗ
		|	&ОбъектМетаданных КАК ПсевдонимЗаданнойТаблицы
		|ГДЕ
		|	&Условие
		|	И ПсевдонимЗаданнойТаблицы.Дата > &ДатаНачалаПроверки
		|	И НЕ ПсевдонимЗаданнойТаблицы.ПометкаУдаления
		|
		|УПОРЯДОЧИТЬ ПО
		|	ПсевдонимЗаданнойТаблицы.Ссылка";
		Запрос.УстановитьПараметр("ДатаНачалаПроверки", ПараметрыПроверки.ДатаНачалаПроверки);
	Иначе
		ТекстЗапроса = 
		"ВЫБРАТЬ ПЕРВЫЕ 1000
		|	ПсевдонимЗаданнойТаблицы.Ссылка КАК ПроблемныйОбъект,
		|	&СсылочныеРеквизиты
		|	,&РеквизитыТабличныхЧастей
		|ИЗ
		|	&ОбъектМетаданных КАК ПсевдонимЗаданнойТаблицы
		|ГДЕ
		|	&Условие
		|	И НЕ ПсевдонимЗаданнойТаблицы.ПометкаУдаления
		|
		|УПОРЯДОЧИТЬ ПО
		|	ПсевдонимЗаданнойТаблицы.Ссылка";
	КонецЕсли;
	
	ИмяТаблицы = СтрЗаменить(ОбъектМетаданных.ПолноеИмя(), ".", "");
	Если ПараметрыПроверки.Свойство("ПроверяемыеОбъекты") И ПараметрыПроверки.ПроверяемыеОбъекты <> Неопределено Тогда
		Условие = ИмяТаблицы + ".Ссылка В (&Ссылка)";
		Запрос.УстановитьПараметр("Ссылка", ПараметрыПроверки.ПроверяемыеОбъекты);
	Иначе
		Условие = ИмяТаблицы + ".Ссылка > &Ссылка";
		Запрос.УстановитьПараметр("Ссылка", "");
	КонецЕсли;
	
	ТекстЗапроса = СтрЗаменить(ТекстЗапроса, "ПсевдонимЗаданнойТаблицы", ИмяТаблицы);
	ТекстЗапроса = СтрЗаменить(ТекстЗапроса, "&Условие", Условие);
	ТекстЗапроса = СтрЗаменить(ТекстЗапроса, "&ОбъектМетаданных", ОбъектМетаданных.ПолноеИмя());
	ТекстЗапроса = СтрЗаменить(ТекстЗапроса, "&СсылочныеРеквизиты", СтрСоединить(СсылочныеРеквизиты, ","));
	
	РеквизитыТабличныхЧастейОбъекта = СсылочныеРеквизитыТабличныхЧастейОбъекта(ОбъектМетаданных);
	Если РеквизитыТабличныхЧастейОбъекта.Количество() > 0 Тогда
		Шаблон = ИмяТаблицы + ".%1.(%2) КАК %1";
		ЗапросТабличныеЧасти = "";
		Для Каждого РеквизитыТабличнойЧасти Из РеквизитыТабличныхЧастейОбъекта Цикл
			ИмяТабличнойЧасти    = РеквизитыТабличнойЧасти.Ключ;
			СтрокаРеквизиты      = СтрСоединить(РеквизитыТабличнойЧасти.Значение, ",");
			ЗаполненныйШаблон    = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
				Шаблон,
				ИмяТабличнойЧасти,
				СтрокаРеквизиты);
			
			Если ЗначениеЗаполнено(ЗапросТабличныеЧасти) Тогда
				ЗапросТабличныеЧасти = ЗапросТабличныеЧасти + "," + Символы.ПС + ЗаполненныйШаблон;
			Иначе
				ЗапросТабличныеЧасти = ЗаполненныйШаблон;
			КонецЕсли;
		КонецЦикла;
		ТекстЗапроса = СтрЗаменить(ТекстЗапроса, "&РеквизитыТабличныхЧастей", ЗапросТабличныеЧасти);
	Иначе
		ТекстЗапроса = СтрЗаменить(ТекстЗапроса, ",&РеквизитыТабличныхЧастей", "");
	КонецЕсли;
	
	Запрос.Текст = ТекстЗапроса;
	Результат = Запрос.Выполнить().Выгрузить();
	
	МаксимальноеКоличество = МаксимальноеКоличествоПроверенныхСсылок();
	ЕстьОтветственный = ОбъектМетаданных.Реквизиты.Найти("Ответственный") <> Неопределено;
	
	Пока Результат.Количество() > 0 Цикл
		
		Для Каждого СтрокаРезультата Из Результат Цикл
			
			УточнениеПроблемы = "";
			
			СсылкаНаОбъект = СтрокаРезультата.ПроблемныйОбъект;
			Для Индекс = 1 По Результат.Колонки.Количество() - 1 Цикл
				ПроверяемаяСсылка = СтрокаРезультата[Индекс];
				Если ЭтоБитаяСсылка(ПроверяемаяСсылка, ПроверенныеСсылки) Тогда
					УточнениеПроблемы = УточнениеПроблемы + ?(ЗначениеЗаполнено(УточнениеПроблемы), Символы.ПС, "")
						+ СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
							НСтр("ru = 'У объекта ""%1"" в реквизите ""%2"" указана ссылка на несуществующий элемент: ""%3"" (%4).'"), 
							СсылкаНаОбъект, 
							Результат.Колонки[Индекс].Имя, 
							ПроверяемаяСсылка,
							ТипЗнч(ПроверяемаяСсылка));
				КонецЕсли;
			КонецЦикла;
			
			Если ПроверенныеСсылки.Количество() >= МаксимальноеКоличество Тогда
				ПроверенныеСсылки.Очистить();
			КонецЕсли;
			
			ЛимитПроблем    = 1;
			ЕщеПроблем      = 0;
			Если РеквизитыТабличныхЧастейОбъекта.Количество() > 0 Тогда
				Для Каждого РеквизитыТабличнойЧасти Из РеквизитыТабличныхЧастейОбъекта Цикл
					
					ТабличнаяЧастьОбъекта = СтрокаРезультата[РеквизитыТабличнойЧасти.Ключ];// ТаблицаЗначений
					ТекущийНомерСтроки    = 1;
					
					Для Каждого СтрокаТЧ Из ТабличнаяЧастьОбъекта Цикл
						Для Каждого ТекущаяКолонка Из ТабличнаяЧастьОбъекта.Колонки Цикл
							ИмяРеквизитаТЧ = ТекущаяКолонка.Имя;
							ПроверяемыеДанные = СтрокаТЧ[ИмяРеквизитаТЧ];
							Если ЭтоБитаяСсылка(ПроверяемыеДанные, ПроверенныеСсылки) Тогда
								Если ЛимитПроблем = 0 Тогда
									ЕщеПроблем = ЕщеПроблем + 1;
									Продолжить;
								КонецЕсли;
								УточнениеПроблемы = УточнениеПроблемы + ?(ЗначениеЗаполнено(УточнениеПроблемы), Символы.ПС, "")
									+ СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
										НСтр("ru = 'У объекта ""%1"" в реквизите ""%2"" табличной части ""%3"" (строка № %4) указана ссылка на несуществующий элемент: ""%5"" (%6).'"),
										СсылкаНаОбъект, ИмяРеквизитаТЧ, СтрЗаменить(РеквизитыТабличнойЧасти.Ключ, ИмяРеквизитаТЧ, ""), 
										ТекущийНомерСтроки, ПроверяемыеДанные, ТипЗнч(ПроверяемыеДанные));
								ЛимитПроблем = ЛимитПроблем - 1;
							КонецЕсли;
						КонецЦикла;
						ТекущийНомерСтроки = ТекущийНомерСтроки + 1;
					КонецЦикла;
					
					Если ПроверенныеСсылки.Количество() >= МаксимальноеКоличество Тогда
						ПроверенныеСсылки.Очистить();
					КонецЕсли;
					
				КонецЦикла;
			КонецЕсли;
			
			Если ЕщеПроблем > 0 Тогда
				УточнениеПроблемы = УточнениеПроблемы + Символы.ПС + НСтр("ru = 'и еще проблем'") + ": " + Строка(ЕщеПроблем);
			КонецЕсли;
			
			Если ПустаяСтрока(УточнениеПроблемы) Тогда
				Продолжить;
			КонецЕсли;
			
			Проблема = ОписаниеПроблемы(СсылкаНаОбъект, ПараметрыПроверки); // @skip-check query-in-loop - порционная проверка ведения учета
			Проблема.УточнениеПроблемы = НСтр("ru = 'Нарушена ссылочная целостность:'") + Символы.ПС + УточнениеПроблемы;
			Если ЕстьОтветственный Тогда
				Проблема.Ответственный = ОбщегоНазначения.ЗначениеРеквизитаОбъекта(СсылкаНаОбъект, "Ответственный");
			КонецЕсли;
			ЗаписатьПроблему(Проблема, ПараметрыПроверки); // @skip-check query-in-loop - порционная проверка ведения учета
			
		КонецЦикла;
		
		Если ПараметрыПроверки.Свойство("ПроверяемыеОбъекты") И ПараметрыПроверки.ПроверяемыеОбъекты <> Неопределено Тогда
			Прервать;
		КонецЕсли;
		
		Запрос.УстановитьПараметр("Ссылка", СтрокаРезультата.ПроблемныйОбъект);
		Результат = Запрос.Выполнить().Выгрузить(); // @skip-check query-in-loop - порционная проверка ведения учета
		
	КонецЦикла;
	
КонецПроцедуры


// Параметры:
//   ОбъектМетаданных - ОбъектМетаданных
// Возвращаемое значение:
//   Массив из Строка
//
Функция СсылочныеРеквизитыОбъекта(ОбъектМетаданных)
	
	Результат = Новый Массив;
	
	ОписаниеСтандартныхРеквизитов = ОбъектМетаданных.СтандартныеРеквизиты;// ОписанияСтандартныхРеквизитов
	Для Каждого СтандартныйРеквизит Из ОписаниеСтандартныхРеквизитов Цикл
		Если СтандартныйРеквизит.Имя = "Ссылка" Или СтандартныйРеквизит.Имя = "ТочкаМаршрута" Тогда
			Продолжить;
		КонецЕсли;
		Если Не СодержитСсылочныйТип(СтандартныйРеквизит) Тогда
			Продолжить;
		КонецЕсли;
		Результат.Добавить(СтандартныйРеквизит.Имя);
	КонецЦикла;
	
	ОписаниеРеквизитов = ОбъектМетаданных.Реквизиты;// Массив из ОбъектМетаданныхРеквизит
	Для Каждого Реквизит Из ОписаниеРеквизитов Цикл
		Если СодержитСсылочныйТип(Реквизит) Тогда
			Результат.Добавить(Реквизит.Имя);
		КонецЕсли;
	КонецЦикла;
	
	Если ОбщегоНазначения.ЭтоЗадача(ОбъектМетаданных) Тогда
		
		РеквизитыАдресации = ОбъектМетаданных.РеквизитыАдресации;// Массив из ОбъектМетаданныхРеквизитАдресации
		Для Каждого РеквизитАдресации Из РеквизитыАдресации Цикл
			Если СодержитСсылочныйТип(РеквизитАдресации) Тогда
				Результат.Добавить(РеквизитАдресации.Имя);
			КонецЕсли;
		КонецЦикла;
		
	КонецЕсли;
	
	Возврат Результат;
	
КонецФункции

Функция СсылочныеРеквизитыТабличныхЧастейОбъекта(ОбъектМетаданных)
	
	Результат = Новый Соответствие;
	Для Каждого ТабличнаяЧасть Из ОбъектМетаданных.ТабличныеЧасти Цикл
		Реквизиты = Новый Массив;
		ОписаниеРеквизитовТабличнойЧасти = ТабличнаяЧасть.Реквизиты;// Массив из ОбъектМетаданныхРеквизит
		Для Каждого РеквизитТабличнойЧасти Из ОписаниеРеквизитовТабличнойЧасти Цикл
			Если СодержитСсылочныйТип(РеквизитТабличнойЧасти) Тогда
				Если РеквизитТабличнойЧасти.Имя = "ТочкаМаршрута" Тогда
					Продолжить;
				КонецЕсли;
				Реквизиты.Добавить(РеквизитТабличнойЧасти.Имя);
			КонецЕсли;
		КонецЦикла;
		Результат.Вставить(ТабличнаяЧасть.Имя, Реквизиты);
	КонецЦикла;
	
	Возврат Результат;
	
КонецФункции

Функция ЭтоБитаяСсылка(ПроверяемыеДанные, ПроверенныеСсылки) 
	
	Если Не ЗначениеЗаполнено(ПроверяемыеДанные) Тогда
		Возврат Ложь;
	КонецЕсли;
	
	ЭтоБитаяСсылка = ПроверенныеСсылки[ПроверяемыеДанные];
	Если ЭтоБитаяСсылка = Ложь Тогда
		Возврат Ложь;
	КонецЕсли;
	
	Если ТипЗнч(ПроверяемыеДанные) = Тип("Число") Тогда
		Возврат Ложь;
	КонецЕсли;
	
	Если ТипЗнч(ПроверяемыеДанные) = Тип("Булево") Тогда
		Возврат Ложь;
	КонецЕсли;
	
	Если ТипЗнч(ПроверяемыеДанные) = Тип("Строка") Тогда
		Возврат Ложь;
	КонецЕсли;
	
	Если ТипЗнч(ПроверяемыеДанные) = Тип("Дата") Тогда
		Возврат Ложь;
	КонецЕсли;
	
	Если ТипЗнч(ПроверяемыеДанные) = Тип("УникальныйИдентификатор") Тогда
		Возврат Ложь;
	КонецЕсли;
	
	Если ТипЗнч(ПроверяемыеДанные) = Тип("ХранилищеЗначения") Тогда
		Возврат Ложь;
	КонецЕсли;
	
	Если Не ОбщегоНазначения.ЗначениеСсылочногоТипа(ПроверяемыеДанные) Тогда
		Возврат Ложь;
	КонецЕсли;
	
	Если ТипЗнч(ПроверяемыеДанные) = Тип("СправочникСсылка.ИдентификаторыОбъектовМетаданных")
		Или ТипЗнч(ПроверяемыеДанные) = Тип("СправочникСсылка.ИдентификаторыОбъектовРасширений") Тогда
		Возврат Ложь;
	КонецЕсли;
	
	Если БизнесПроцессы.ТипВсеСсылкиТочекМаршрутаБизнесПроцессов().СодержитТип(ТипЗнч(ПроверяемыеДанные)) Тогда
		Возврат Ложь;
	КонецЕсли;
	
	Если ЭтоБитаяСсылка = Неопределено Тогда
		ЭтоБитаяСсылка = Не ОбщегоНазначения.СсылкаСуществует(ПроверяемыеДанные);
		ПроверенныеСсылки[ПроверяемыеДанные] = ЭтоБитаяСсылка;
	КонецЕсли;
	
	Возврат ЭтоБитаяСсылка;
	
КонецФункции

Функция МаксимальноеКоличествоПроверенныхСсылок()
	
	Возврат 100000;
	
КонецФункции	

#Область КонтрольСсылочнойЦелостностиВРегистрах

Процедура НайтиБитыеСсылкиВРегистрах(ОбъектМетаданных, ПараметрыПроверки, ПроверенныеСсылки)
	
	Если ОбъектМетаданных.Измерения.Количество() = 0 Тогда
		Возврат;
	КонецЕсли;
	
	Если ОбщегоНазначения.ЭтоРегистрНакопления(ОбъектМетаданных) Тогда
		НайтиБитыеСсылкиВРегистрахНакопления(ОбъектМетаданных, ПараметрыПроверки, ПроверенныеСсылки);
	ИначеЕсли ОбщегоНазначения.ЭтоРегистрСведений(ОбъектМетаданных) Тогда
		НайтиБитыеСсылкиВРегистрахСведений(ОбъектМетаданных, ПараметрыПроверки, ПроверенныеСсылки);
	ИначеЕсли ОбщегоНазначения.ЭтоРегистрБухгалтерии(ОбъектМетаданных) Тогда
		НайтиБитыеСсылкиВРегистрахБухгалтерии(ОбъектМетаданных, ПараметрыПроверки, ВидыСубконто(ОбъектМетаданных), ПроверенныеСсылки);
	ИначеЕсли ОбщегоНазначения.ЭтоРегистрРасчета(ОбъектМетаданных) Тогда
		НайтиБитыеСсылкиВРегистрахРасчета(ОбъектМетаданных, ПараметрыПроверки, ПроверенныеСсылки);
	КонецЕсли;
	
КонецПроцедуры

Процедура НайтиБитыеСсылкиВРегистрахНакопления(ОбъектМетаданных, ПараметрыПроверки, ПроверенныеСсылки)
	
	МаксимальноеКоличество = МаксимальноеКоличествоПроверенныхСсылок();
	
	ПолноеИмя         = ОбъектМетаданных.ПолноеИмя();
	РеквизитыРегистра = СсылочныеРеквизитыРегистра(ОбъектМетаданных);
	
	ТекстЗапроса =
	"ВЫБРАТЬ ПЕРВЫЕ 1000
	|	ОбъектМетаданных.Регистратор КАК РегистраторРеквизитСсылка,
	|	ОбъектМетаданных.Период КАК Период
	|ИЗ
	|	&ОбъектМетаданных КАК ОбъектМетаданных
	|ГДЕ
	|	ОбъектМетаданных.Период > &ДатаНачалаПроверки
	|
	|СГРУППИРОВАТЬ ПО
	|	ОбъектМетаданных.Период,
	|	ОбъектМетаданных.Регистратор
	|
	|УПОРЯДОЧИТЬ ПО
	|	ОбъектМетаданных.Период";
	
	ТекстЗапроса = СтрЗаменить(ТекстЗапроса, "&ОбъектМетаданных", ПолноеИмя);
	
	Запрос = Новый Запрос(ТекстЗапроса);
	Запрос.УстановитьПараметр("ДатаНачалаПроверки", ПараметрыПроверки.ДатаНачалаПроверки);
	Результат = Запрос.Выполнить().Выгрузить();
	
	МенеджерРегистра = ОбщегоНазначения.МенеджерОбъектаПоПолномуИмени(ПолноеИмя);
	
	Пока Результат.Количество() > 0 Цикл
		
		Для Каждого СтрокаРезультата Из Результат Цикл
			
			ТекущийНаборЗаписей = МенеджерРегистра.СоздатьНаборЗаписей();// РегистрСведенийНаборЗаписей
			ТекущийНаборЗаписей.Отбор.Регистратор.Установить(СтрокаРезультата.РегистраторРеквизитСсылка);
			ТекущийНаборЗаписей.Прочитать();
			
			НомераПроблемныхЗаписей = "";
			Для Каждого ТекущаяЗапись Из ТекущийНаборЗаписей Цикл
				Для Каждого ИмяРеквизита Из РеквизитыРегистра Цикл
					Если ЭтоБитаяСсылка(ТекущаяЗапись[ИмяРеквизита], ПроверенныеСсылки) Тогда
						НомераПроблемныхЗаписей = НомераПроблемныхЗаписей + ?(ЗначениеЗаполнено(НомераПроблемныхЗаписей), ", ", "")
							+ Формат(ТекущийНаборЗаписей.Индекс(ТекущаяЗапись) + 1, "ЧГ=0");
					КонецЕсли;
				КонецЦикла;
			КонецЦикла;
			
			Если ПроверенныеСсылки.Количество() >= МаксимальноеКоличество Тогда
				ПроверенныеСсылки.Очистить();
			КонецЕсли;
			
			Если ПустаяСтрока(НомераПроблемныхЗаписей) Тогда
				Продолжить;
			КонецЕсли;
			
			Проблема = ОписаниеПроблемы(ОбщегоНазначения.ИдентификаторОбъектаМетаданных(ОбъектМетаданных), ПараметрыПроверки); // @skip-check query-in-loop - порционная проверка ведения учета
			Проблема.УточнениеПроблемы = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
				НСтр("ru = 'У регистра накопления ""%1"" в записях с номерами %2 по регистратору ""%3"" указаны ссылки на несуществующие данные.'"),
				ОбъектМетаданных.Представление(), НомераПроблемныхЗаписей, СтрокаРезультата.РегистраторРеквизитСсылка);
				
			ДополнительнаяИнформация = Новый Структура;
			ДополнительнаяИнформация.Вставить("Регистратор", СтрокаРезультата.РегистраторРеквизитСсылка);
			Проблема.ДополнительнаяИнформация = Новый ХранилищеЗначения(ДополнительнаяИнформация);
			
			ЗаписатьПроблему(Проблема, ПараметрыПроверки); // @skip-check query-in-loop - порционная проверка ведения учета
			
		КонецЦикла;
		
		Запрос.УстановитьПараметр("ДатаНачалаПроверки", СтрокаРезультата.Период);
		Результат = Запрос.Выполнить().Выгрузить(); // @skip-check query-in-loop - порционная проверка ведения учета
		
	КонецЦикла;
	
КонецПроцедуры

Процедура НайтиБитыеСсылкиВРегистрахСведений(ОбъектМетаданных, ПараметрыПроверки, ПроверенныеСсылки)
	
	Если ОбъектМетаданных.РежимЗаписи = Метаданные.СвойстваОбъектов.РежимЗаписиРегистра.ПодчинениеРегистратору Тогда
		НайтиБитыеСсылкиВПодчиненныхРегистрахСведений(ОбъектМетаданных, ПараметрыПроверки, ПроверенныеСсылки);
	ИначеЕсли ОбъектМетаданных.ПериодичностьРегистраСведений <> Метаданные.СвойстваОбъектов.ПериодичностьРегистраСведений.Непериодический Тогда
		НайтиБитыеСсылкиВНезависимыхПериодическихРегистрахСведений(ОбъектМетаданных, ПараметрыПроверки, ПроверенныеСсылки);
	Иначе
		НайтиБитыеСсылкиВНезависимыхНеПериодическихРегистрахСведений(ОбъектМетаданных, ПараметрыПроверки, ПроверенныеСсылки);
	КонецЕсли;
	
КонецПроцедуры

Процедура НайтиБитыеСсылкиВПодчиненныхРегистрахСведений(ОбъектМетаданных, ПараметрыПроверки, ПроверенныеСсылки)
	
	МаксимальноеКоличество = МаксимальноеКоличествоПроверенныхСсылок();
	ПолноеИмя         = ОбъектМетаданных.ПолноеИмя();
	РеквизитыРегистра = СсылочныеРеквизитыРегистра(ОбъектМетаданных);
	
	ЭтотРегистрПериодический = ОбъектМетаданных.ПериодичностьРегистраСведений <> Метаданные.СвойстваОбъектов.ПериодичностьРегистраСведений.Непериодический;
	Если ЭтотРегистрПериодический Тогда
		
		Запрос = Новый Запрос(
		"ВЫБРАТЬ ПЕРВЫЕ 1000
		|	ПсевдонимЗаданнойТаблицы.Регистратор КАК РегистраторРеквизитСсылка,
		|	ПсевдонимЗаданнойТаблицы.Период КАК Период
		|ИЗ
		|	&ОбъектМетаданных КАК ПсевдонимЗаданнойТаблицы
		|ГДЕ
		|	ПсевдонимЗаданнойТаблицы.Период > &ДатаНачалаПроверки
		|
		|СГРУППИРОВАТЬ ПО
		|	ПсевдонимЗаданнойТаблицы.Период,
		|	ПсевдонимЗаданнойТаблицы.Регистратор
		|
		|УПОРЯДОЧИТЬ ПО
		|	ПсевдонимЗаданнойТаблицы.Период");
		
		Запрос.УстановитьПараметр("ДатаНачалаПроверки", ПараметрыПроверки.ДатаНачалаПроверки);
		
	Иначе
		
		Запрос = Новый Запрос(
		"ВЫБРАТЬ ПЕРВЫЕ 1000
		|	ПсевдонимЗаданнойТаблицы.Регистратор КАК РегистраторРеквизитСсылка
		|ИЗ
		|	&ОбъектМетаданных КАК ПсевдонимЗаданнойТаблицы
		|ГДЕ
		|	ПсевдонимЗаданнойТаблицы.Регистратор > &Регистратор
		|
		|СГРУППИРОВАТЬ ПО
		|	ПсевдонимЗаданнойТаблицы.Регистратор
		|
		|УПОРЯДОЧИТЬ ПО
		|	ПсевдонимЗаданнойТаблицы.Регистратор");
		
		Запрос.УстановитьПараметр("Регистратор", "");
		
	КонецЕсли;
	
	ИмяТаблицы = СтрЗаменить(ПолноеИмя, ".", "");
	Запрос.Текст = СтрЗаменить(Запрос.Текст, "ПсевдонимЗаданнойТаблицы", ИмяТаблицы);
	Запрос.Текст = СтрЗаменить(Запрос.Текст, "&ОбъектМетаданных", ПолноеИмя);
	Результат    = Запрос.Выполнить().Выгрузить();
	
	МенеджерРегистра = ОбщегоНазначения.МенеджерОбъектаПоПолномуИмени(ПолноеИмя);
	
	Пока Результат.Количество() > 0 Цикл
		
		Для Каждого СтрокаРезультата Из Результат Цикл
			
			ТекущийНаборЗаписей = МенеджерРегистра.СоздатьНаборЗаписей();// РегистрСведенийНаборЗаписей
			ТекущийНаборЗаписей.Отбор.Регистратор.Установить(СтрокаРезультата.РегистраторРеквизитСсылка);
			ТекущийНаборЗаписей.Прочитать();
			
			НомераПроблемныхЗаписей = "";
			Для Каждого ТекущаяЗапись Из ТекущийНаборЗаписей Цикл
				Для Каждого ИмяРеквизита Из РеквизитыРегистра Цикл
					Если ЭтоБитаяСсылка(ТекущаяЗапись[ИмяРеквизита], ПроверенныеСсылки) Тогда
						НомераПроблемныхЗаписей = НомераПроблемныхЗаписей + ?(ЗначениеЗаполнено(НомераПроблемныхЗаписей), ", ", "")
							+ Формат(ТекущийНаборЗаписей.Индекс(ТекущаяЗапись) + 1, "ЧГ=0");
					КонецЕсли;
				КонецЦикла;
			КонецЦикла;
			
			Если ПроверенныеСсылки.Количество() >= МаксимальноеКоличество Тогда
				ПроверенныеСсылки.Очистить();
			КонецЕсли;
			
			Если ПустаяСтрока(НомераПроблемныхЗаписей) Тогда
				Продолжить;
			КонецЕсли;
			
			Проблема = ОписаниеПроблемы(ОбщегоНазначения.ИдентификаторОбъектаМетаданных(ОбъектМетаданных), ПараметрыПроверки); // @skip-check query-in-loop - порционная проверка ведения учета
			Проблема.УточнениеПроблемы = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
				НСтр("ru = 'У регистра сведений ""%1"" в записях с номерами %2 по регистратору ""%3"" указаны ссылки на несуществующие данные.'"),
				ОбъектМетаданных.Представление(), НомераПроблемныхЗаписей, СтрокаРезультата.РегистраторРеквизитСсылка);
				
			ДополнительнаяИнформация = Новый Структура;
			ДополнительнаяИнформация.Вставить("Регистратор", СтрокаРезультата.РегистраторРеквизитСсылка);
			Проблема.ДополнительнаяИнформация = Новый ХранилищеЗначения(ДополнительнаяИнформация);
			
			ЗаписатьПроблему(Проблема, ПараметрыПроверки); // @skip-check query-in-loop - порционная проверка ведения учета
			
		КонецЦикла;
		
		Если ЭтотРегистрПериодический Тогда
			Запрос.УстановитьПараметр("ДатаНачалаПроверки", СтрокаРезультата.Период);
		Иначе
			Запрос.УстановитьПараметр("Регистратор", СтрокаРезультата.РегистраторРеквизитСсылка);
		КонецЕсли;
		
		Результат = Запрос.Выполнить().Выгрузить(); // @skip-check query-in-loop - порционная проверка ведения учета
		
	КонецЦикла;
	
КонецПроцедуры

Процедура НайтиБитыеСсылкиВНезависимыхПериодическихРегистрахСведений(ОбъектМетаданных, ПараметрыПроверки, ПроверенныеСсылки)
	
	МаксимальноеКоличество = МаксимальноеКоличествоПроверенныхСсылок();
	ИнформацияОНезависимомРегистре = ИнформацияОНезависимомРегистре(ОбъектМетаданных);
	ПоляВыборки         = ИнформацияОНезависимомРегистре.ПоляВыборки;
	ИнформацияОРегистре = ИнформацияОНезависимомРегистре.ИнформацияОРегистре;
	
	ИмяТаблицы = СтрЗаменить(ОбъектМетаданных.ПолноеИмя(), ".", "");
	УсловиеПоИзмерениям = "";
	ПоляУпорядочивания = ИмяТаблицы + "." + "Период";
	Измерения          = ОбъектМетаданных.Измерения;// Массив из ОбъектМетаданныхИзмерение
	
	Для Каждого Измерение Из Измерения Цикл
		УсловиеПоИзмерениям = УсловиеПоИзмерениям + ?(ЗначениеЗаполнено(УсловиеПоИзмерениям), " И ", "") + ИмяТаблицы + "." + Измерение.Имя + " >= &" + Измерение.Имя;
		ПоляУпорядочивания = ПоляУпорядочивания + ?(ЗначениеЗаполнено(ПоляУпорядочивания), ", ", "") + ИмяТаблицы + "." + Измерение.Имя;
	КонецЦикла;
	
	ТекстЗапроса =
	"ВЫБРАТЬ ПЕРВЫЕ 1000
	|	ПсевдонимЗаданнойТаблицы.Период КАК Период,
	|	&ПоляВыборки КАК ПоляВыборки
	|ИЗ
	|	&ОбъектМетаданных КАК ПсевдонимЗаданнойТаблицы
	|ГДЕ
	|	(ПсевдонимЗаданнойТаблицы.Период > &Период
	|				И НЕ &ТолькоУказанныйПериод
	|			ИЛИ ПсевдонимЗаданнойТаблицы.Период = &Период
	|				И &ТолькоУказанныйПериод)
	|	И &Условие
	|
	|УПОРЯДОЧИТЬ ПО
	|	&ПоляУпорядочивания";
	
	ТекстЗапроса = СтрЗаменить(ТекстЗапроса, "&ПоляУпорядочивания", ПоляУпорядочивания);
	ТекстЗапроса = СтрЗаменить(ТекстЗапроса, "&ПоляВыборки КАК ПоляВыборки", ПоляВыборки);
	ТекстЗапроса = СтрЗаменить(ТекстЗапроса, "&ОбъектМетаданных", ОбъектМетаданных.ПолноеИмя());
	ТекстЗапроса = СтрЗаменить(ТекстЗапроса, "ПсевдонимЗаданнойТаблицы", ИмяТаблицы);
	
	ТекстПервогоЗапроса = СтрЗаменить(ТекстЗапроса, "&Условие", "Истина");
	ТекстЗапросаСУсловием = СтрЗаменить(ТекстЗапроса, "&Условие", УсловиеПоИзмерениям);
	
	Запрос = Новый Запрос(ТекстПервогоЗапроса);
	Запрос.УстановитьПараметр("Период", ПараметрыПроверки.ДатаНачалаПроверки);
	Запрос.УстановитьПараметр("ТолькоУказанныйПериод", Ложь);
	Результат = Запрос.Выполнить().Выгрузить();
	ЭтоПервыйПроход = Истина;
	
	Пока Результат.Количество() > 0 Цикл
		
		// Последняя запись уже проверена на предыдущей итерации.
		Если Не ЭтоПервыйПроход И Результат.Количество() = 1 Тогда 
			Прервать;
		КонецЕсли;
		
		Для Каждого СтрокаРезультата Из Результат Цикл
			
			Если Не ЭтоПервыйПроход И Результат.Индекс(СтрокаРезультата) = 0 Тогда
				Продолжить;
			КонецЕсли;
			Для Каждого ИнформацияОРеквизите Из ИнформацияОРегистре Цикл
				
				ТекущаяСсылка = СтрокаРезультата[ИнформацияОРеквизите.ИмяМетаданных + ИнформацияОРеквизите.ТипМетаданныхВИменительномПадеже + "Ссылка"];
				Если Не ЭтоБитаяСсылка(ТекущаяСсылка, ПроверенныеСсылки) Тогда
					Продолжить;
				КонецЕсли;
				
				ДополнительнаяИнформация = Новый Структура;
				ДополнительнаяИнформация.Вставить("Период", СтрокаРезультата.Период);
				Для Каждого Измерение Из Измерения Цикл
					ИзмерениеСсылка = СтрокаРезультата[Измерение.Имя + "ИзмерениеСсылка"];
					ДополнительнаяИнформация.Вставить(Измерение.Имя, ИзмерениеСсылка);
				КонецЦикла;
				
				БитаяСсылка = СтрокаРезультата[ИнформацияОРеквизите.ИмяМетаданных + ИнформацияОРеквизите.ТипМетаданныхВИменительномПадеже + "Ссылка"];
				Проблема = ОписаниеПроблемы(ОбщегоНазначения.ИдентификаторОбъектаМетаданных(ОбъектМетаданных), ПараметрыПроверки); // @skip-check query-in-loop - порционная проверка ведения учета
				Проблема.УточнениеПроблемы = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
					НСтр("ru = 'У регистра сведений ""%1"" в %2, по комбинации измерений ""%3"" указана ссылка на несуществующий элемент: ""%4"" (%5).'"),
					ОбъектМетаданных.Представление(), ИнформацияОРеквизите.ТипМетаданныхВТворительномПадеже,
					ПоляУпорядочивания, 
					БитаяСсылка,
					ТипЗнч(БитаяСсылка));
				Проблема.ДополнительнаяИнформация = Новый ХранилищеЗначения(ДополнительнаяИнформация);
				ЗаписатьПроблему(Проблема, ПараметрыПроверки); // @skip-check query-in-loop - порционная проверка ведения учета
				
			КонецЦикла;
			
			Если ПроверенныеСсылки.Количество() >= МаксимальноеКоличество Тогда
				ПроверенныеСсылки.Очистить();
			КонецЕсли;
			
		КонецЦикла;
		
		Если ЭтоПервыйПроход Тогда
			ЭтоПервыйПроход = Ложь;
		КонецЕсли;
		
		// После прохода по всем периодам делаем дообработку по последнему периоду.
		Запрос.Текст = ТекстЗапросаСУсловием;
		Запрос.УстановитьПараметр("Период", СтрокаРезультата["Период"]);
		Запрос.УстановитьПараметр("ТолькоУказанныйПериод", Истина);
		Для Каждого Измерение Из Измерения Цикл
			Запрос.УстановитьПараметр(Измерение.Имя, СтрокаРезультата[Измерение.Имя + "ИзмерениеСсылка"]);
		КонецЦикла;
		Результат = Запрос.Выполнить().Выгрузить(); // @skip-check query-in-loop - порционная проверка ведения учета
		// Если по последнему периоду все записи обработаны, то выбираем все записи по следующим периодам.
		Если Результат.Количество() = 0 Или Результат.Количество() = 1 Тогда
			Запрос.Текст = ТекстПервогоЗапроса;
			Запрос.УстановитьПараметр("Период", СтрокаРезультата["Период"]);
			Запрос.УстановитьПараметр("ТолькоУказанныйПериод", Ложь);
			Результат = Запрос.Выполнить().Выгрузить(); // @skip-check query-in-loop - порционная проверка ведения учета
			ЭтоПервыйПроход = Истина;
		КонецЕсли;
		
	КонецЦикла;
	
КонецПроцедуры

// Параметры:
//   ОбъектМетаданных - ОбъектМетаданныхРегистрСведений
//   ПараметрыПроверки - см. КонтрольВеденияУчета.ОписаниеПроблемы
//   ПроверенныеСсылки - Соответствие
//
Процедура НайтиБитыеСсылкиВНезависимыхНеПериодическихРегистрахСведений(ОбъектМетаданных, ПараметрыПроверки, ПроверенныеСсылки)
	
	МаксимальноеКоличество = МаксимальноеКоличествоПроверенныхСсылок();
	ИнформацияОНезависимомРегистре = ИнформацияОНезависимомРегистре(ОбъектМетаданных);
	ПоляВыборки         = ИнформацияОНезависимомРегистре.ПоляВыборки;
	ИнформацияОРегистре = ИнформацияОНезависимомРегистре.ИнформацияОРегистре;
	
	ИмяТаблицы = СтрЗаменить(ОбъектМетаданных.ПолноеИмя(), ".", "");
	УсловиеПоИзмерениям = "";
	ПоляУпорядочивания  = "";
	
	ОписаниеИзмерений = ОбъектМетаданных.Измерения;
	Для Каждого Измерение Из ОписаниеИзмерений Цикл
		УсловиеПоИзмерениям = УсловиеПоИзмерениям + ?(ЗначениеЗаполнено(УсловиеПоИзмерениям), " И ", "") + ИмяТаблицы + "." + Измерение.Имя + " >= &" + Измерение.Имя;
		ПоляУпорядочивания  = ПоляУпорядочивания + ?(ЗначениеЗаполнено(ПоляУпорядочивания), ", ", "") + ИмяТаблицы + "." + Измерение.Имя;
	КонецЦикла;
	
	ТекстЗапроса =
	"ВЫБРАТЬ ПЕРВЫЕ 1000
	|	&ПоляВыборки КАК ПоляВыборки
	|ИЗ
	|	&ОбъектМетаданных КАК ПсевдонимЗаданнойТаблицы
	|ГДЕ
	|	&Условие
	|
	|УПОРЯДОЧИТЬ ПО
	|	&ПоляУпорядочивания";
	
	ТекстЗапроса = СтрЗаменить(ТекстЗапроса, "&ПоляУпорядочивания", ПоляУпорядочивания);
	ТекстЗапроса = СтрЗаменить(ТекстЗапроса, "&ПоляВыборки КАК ПоляВыборки", ПоляВыборки);
	ТекстЗапроса = СтрЗаменить(ТекстЗапроса, "&ОбъектМетаданных", ОбъектМетаданных.ПолноеИмя());
	ТекстЗапроса = СтрЗаменить(ТекстЗапроса, "ПсевдонимЗаданнойТаблицы", ИмяТаблицы);
	
	ТекстПервогоЗапроса   = СтрЗаменить(ТекстЗапроса, "&Условие", "Истина");
	ТекстЗапросаСУсловием = СтрЗаменить(ТекстЗапроса, "&Условие", УсловиеПоИзмерениям);
	
	Запрос = Новый Запрос(ТекстПервогоЗапроса);
	
	Результат       = Запрос.Выполнить().Выгрузить();
	ЭтоПервыйПроход = Истина;
	
	Пока Результат.Количество() > 0 Цикл
		
		// Последняя запись уже проверена на предыдущей итерации.
		Если Не ЭтоПервыйПроход И Результат.Количество() = 1 Тогда
			Прервать;
		КонецЕсли;
		
		Для Каждого СтрокаРезультата Из Результат Цикл
			
			Если Не ЭтоПервыйПроход И Результат.Индекс(СтрокаРезультата) = 0 Тогда
				Продолжить;
			КонецЕсли;
			
			Для Каждого ИнформацияОРеквизите Из ИнформацияОРегистре Цикл
				
				ТекущаяСсылка = СтрокаРезультата[ИнформацияОРеквизите.ИмяМетаданных + ИнформацияОРеквизите.ТипМетаданныхВИменительномПадеже + "Ссылка"];
				Если Не ЭтоБитаяСсылка(ТекущаяСсылка, ПроверенныеСсылки) Тогда
					Продолжить;
				КонецЕсли;
				
				ДополнительнаяИнформация = Новый Структура;
				Для Каждого Измерение Из ОписаниеИзмерений Цикл
					ИзмерениеСсылка = СтрокаРезультата[Измерение.Имя + "ИзмерениеСсылка"];
					ДополнительнаяИнформация.Вставить(Измерение.Имя, ИзмерениеСсылка);
				КонецЦикла;
				
				БитаяСсылка = СтрокаРезультата[ИнформацияОРеквизите.ИмяМетаданных + ИнформацияОРеквизите.ТипМетаданныхВИменительномПадеже + "Ссылка"];
				Проблема = ОписаниеПроблемы(ОбщегоНазначения.ИдентификаторОбъектаМетаданных(ОбъектМетаданных), ПараметрыПроверки); // @skip-check query-in-loop - порционная проверка ведения учета
				Проблема.УточнениеПроблемы = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
					НСтр("ru = 'У регистра сведений ""%1"" в %2, по комбинации измерений ""%3"" указана ссылка на несуществующий элемент: ""%4"" (%5).'"),
					ОбъектМетаданных.Представление(), ИнформацияОРеквизите.ТипМетаданныхВТворительномПадеже,
					ПоляУпорядочивания, 
					БитаяСсылка,
					ТипЗнч(БитаяСсылка));
				Проблема.ДополнительнаяИнформация = Новый ХранилищеЗначения(ДополнительнаяИнформация);
				ЗаписатьПроблему(Проблема, ПараметрыПроверки); // @skip-check query-in-loop - порционная проверка ведения учета
				
			КонецЦикла;
			
		КонецЦикла;
		
		Если ПроверенныеСсылки.Количество() >= МаксимальноеКоличество Тогда
			ПроверенныеСсылки.Очистить();
		КонецЕсли;
		
		Если ЭтоПервыйПроход Тогда
			ЭтоПервыйПроход = Ложь;
			Запрос.Текст = ТекстЗапросаСУсловием;
		КонецЕсли;
		
		Для Каждого Измерение Из ОписаниеИзмерений Цикл
			Запрос.УстановитьПараметр(Измерение.Имя, СтрокаРезультата[Измерение.Имя + "ИзмерениеСсылка"]);
		КонецЦикла;
		
		Результат = Запрос.Выполнить().Выгрузить(); // @skip-check query-in-loop - порционная проверка ведения учета
		
	КонецЦикла;
	
КонецПроцедуры

// Параметры:
//   ОбъектМетаданных - ОбъектМетаданныхРегистрБухгалтерии
//   ПараметрыПроверки - см. КонтрольВеденияУчета.ОписаниеПроблемы
//   ВидыСубконто - Массив
//   ПроверенныеСсылки - Соответствие
//
Процедура НайтиБитыеСсылкиВРегистрахБухгалтерии(ОбъектМетаданных, ПараметрыПроверки, ВидыСубконто, ПроверенныеСсылки)
	
	МаксимальноеКоличество = МаксимальноеКоличествоПроверенныхСсылок();
	ПолноеИмя                      = ОбъектМетаданных.ПолноеИмя();
	
	СсылочныеРеквизиты = Новый Массив;
	
	Если Не ОбъектМетаданных.Корреспонденция Тогда
		Если ОбъектМетаданных.ПланСчетов <> Неопределено Тогда
			СсылочныеРеквизиты.Добавить("Счет");
		КонецЕсли;
	Иначе
		СсылочныеРеквизиты.Добавить("СчетДт");
		СсылочныеРеквизиты.Добавить("СчетКт");
	КонецЕсли;
		
	Для Каждого Измерение Из ОбъектМетаданных.Измерения Цикл
		
		Если Не СодержитСсылочныйТип(Измерение) Тогда
			Продолжить;
		КонецЕсли;
		
		Если Измерение.Балансовый Или Не ОбъектМетаданных.Корреспонденция Тогда
			СсылочныеРеквизиты.Добавить(Измерение.Имя);
		Иначе
			СсылочныеРеквизиты.Добавить(Измерение.Имя + "Дт");
			СсылочныеРеквизиты.Добавить(Измерение.Имя + "Кт");
		КонецЕсли;
		
	КонецЦикла;
	
	Для Каждого Реквизит Из ОбъектМетаданных.Реквизиты Цикл
		Если Не СодержитСсылочныйТип(Реквизит) Тогда
			Продолжить;
		КонецЕсли;
		СсылочныеРеквизиты.Добавить(Реквизит.Имя);
	КонецЦикла;
	
	Запрос = Новый Запрос(
	"ВЫБРАТЬ
	|	ОбъектМетаданных.Регистратор КАК РегистраторРеквизитСсылка,
	|	ОбъектМетаданных.Период КАК Период
	|ИЗ
	|	&ОбъектМетаданных КАК ОбъектМетаданных
	|
	|СГРУППИРОВАТЬ ПО
	|	ОбъектМетаданных.Период,
	|	ОбъектМетаданных.Регистратор
	|
	|УПОРЯДОЧИТЬ ПО
	|	ОбъектМетаданных.Период");
	
	Запрос.Текст = СтрЗаменить(Запрос.Текст, "&ОбъектМетаданных", ПолноеИмя + ".ДвиженияССубконто(, , Период > &ДатаНачалаПроверки, , 1000)");
	Запрос.УстановитьПараметр("ДатаНачалаПроверки", ПараметрыПроверки.ДатаНачалаПроверки);
	Результат = Запрос.Выполнить().Выгрузить();
	
	МенеджерРегистра = ОбщегоНазначения.МенеджерОбъектаПоПолномуИмени(ПолноеИмя);
	
	Пока Результат.Количество() > 0 Цикл
		
		Для Каждого СтрокаРезультата Из Результат Цикл
			
			ТекущийНаборЗаписей = МенеджерРегистра.СоздатьНаборЗаписей();
			ТекущийНаборЗаписей.Отбор.Регистратор.Установить(СтрокаРезультата.РегистраторРеквизитСсылка);
			ТекущийНаборЗаписей.Прочитать();
			
			НомераПроблемныхЗаписей = "";
			Для Каждого ТекущаяЗапись Из ТекущийНаборЗаписей Цикл
				
				ПроверяемыеОбъекты = Новый Массив;
				
				Для Каждого ВидСубконто Из ВидыСубконто Цикл
					Если Не ОбъектМетаданных.Корреспонденция Тогда
						ПроверяемыеОбъекты.Добавить(ТекущаяЗапись.Субконто[ВидСубконто]);
					Иначе
						ПроверяемыеОбъекты.Добавить(ТекущаяЗапись.СубконтоДт[ВидСубконто]);
						ПроверяемыеОбъекты.Добавить(ТекущаяЗапись.СубконтоКт[ВидСубконто]);
					КонецЕсли;
				КонецЦикла;
				
				Для Каждого ИмяРеквизита Из СсылочныеРеквизиты Цикл
					ПроверяемыеОбъекты.Добавить(ТекущаяЗапись[ИмяРеквизита]);
				КонецЦикла;
				
				Для Каждого ПроверяемыйОбъект Из ПроверяемыеОбъекты Цикл
					Если ЭтоБитаяСсылка(ПроверяемыйОбъект, ПроверенныеСсылки) Тогда
						НомераПроблемныхЗаписей = НомераПроблемныхЗаписей + ?(ЗначениеЗаполнено(НомераПроблемныхЗаписей), ", ", "")
							+ Формат(ТекущийНаборЗаписей.Индекс(ТекущаяЗапись) + 1, "ЧГ=0");
					КонецЕсли;	
				КонецЦикла;
				
			КонецЦикла;
			
			Если ПроверенныеСсылки.Количество() >= МаксимальноеКоличество Тогда
				ПроверенныеСсылки.Очистить();
			КонецЕсли;
			
			Если ПустаяСтрока(НомераПроблемныхЗаписей) Тогда
				Продолжить;
			КонецЕсли;
			
			Проблема = ОписаниеПроблемы(ОбщегоНазначения.ИдентификаторОбъектаМетаданных(ОбъектМетаданных), ПараметрыПроверки); // @skip-check query-in-loop - порционная проверка ведения учета
			Проблема.УточнениеПроблемы = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
				НСтр("ru = 'У регистра бухгалтерии ""%1"" в записях с номерами %2 по регистратору ""%3"" указаны ссылки на несуществующие данные.'"),
				ОбъектМетаданных.Представление(), НомераПроблемныхЗаписей, СтрокаРезультата.РегистраторРеквизитСсылка);
				
			ДополнительнаяИнформация = Новый Структура;
			ДополнительнаяИнформация.Вставить("Регистратор", СтрокаРезультата.РегистраторРеквизитСсылка);
			Проблема.ДополнительнаяИнформация = Новый ХранилищеЗначения(ДополнительнаяИнформация);
			
			ЗаписатьПроблему(Проблема, ПараметрыПроверки); // @skip-check query-in-loop - порционная проверка ведения учета
			
		КонецЦикла;
		
		Запрос.УстановитьПараметр("ДатаНачалаПроверки", СтрокаРезультата.Период);
		Результат = Запрос.Выполнить().Выгрузить(); // @skip-check query-in-loop - порционная проверка ведения учета
		
	КонецЦикла;
	
КонецПроцедуры

// Параметры:
//   ОбъектМетаданных - ОбъектМетаданныхРегистрРасчета
//   ПараметрыПроверки - см. КонтрольВеденияУчета.ОписаниеПроблемы
//   ПроверенныеСсылки - Соответствие
//
Процедура НайтиБитыеСсылкиВРегистрахРасчета(ОбъектМетаданных, ПараметрыПроверки, ПроверенныеСсылки)
	
	МаксимальноеКоличество = МаксимальноеКоличествоПроверенныхСсылок();
	ПолноеИмя         = ОбъектМетаданных.ПолноеИмя();
	РеквизитыРегистра = СсылочныеРеквизитыРегистра(ОбъектМетаданных);
	
	Запрос = Новый Запрос(
	"ВЫБРАТЬ ПЕРВЫЕ 1000
	|	ОбъектМетаданных.Регистратор КАК РегистраторРеквизитСсылка,
	|	ОбъектМетаданных.ПериодРегистрации КАК Период
	|ИЗ
	|	&ОбъектМетаданных КАК ОбъектМетаданных
	|ГДЕ
	|	ОбъектМетаданных.ПериодРегистрации > &ДатаНачалаПроверки
	|
	|СГРУППИРОВАТЬ ПО
	|	ОбъектМетаданных.ПериодРегистрации,
	|	ОбъектМетаданных.Регистратор
	|
	|УПОРЯДОЧИТЬ ПО
	|	ОбъектМетаданных.ПериодРегистрации");
	
	Запрос.Текст = СтрЗаменить(Запрос.Текст, "&ОбъектМетаданных", ПолноеИмя);
	Запрос.УстановитьПараметр("ДатаНачалаПроверки", ПараметрыПроверки.ДатаНачалаПроверки);
	Результат = Запрос.Выполнить().Выгрузить();
	
	МенеджерРегистра = ОбщегоНазначения.МенеджерОбъектаПоПолномуИмени(ПолноеИмя);
	
	Пока Результат.Количество() > 0 Цикл
		
		Для Каждого СтрокаРезультата Из Результат Цикл
			
			ТекущийНаборЗаписей = МенеджерРегистра.СоздатьНаборЗаписей();
			ТекущийНаборЗаписей.Отбор.Регистратор.Установить(СтрокаРезультата.РегистраторРеквизитСсылка);
			ТекущийНаборЗаписей.Прочитать();
			
			НомераПроблемныхЗаписей = "";
			Для Каждого ТекущаяЗапись Из ТекущийНаборЗаписей Цикл
				Для Каждого ИмяРеквизита Из РеквизитыРегистра Цикл
					ТекущаяСсылка = ТекущаяЗапись[ИмяРеквизита];
					Если ЭтоБитаяСсылка(ТекущаяСсылка, ПроверенныеСсылки) Тогда
						НомераПроблемныхЗаписей = НомераПроблемныхЗаписей + ?(ЗначениеЗаполнено(НомераПроблемныхЗаписей), ", ", "")
							+ Формат(ТекущийНаборЗаписей.Индекс(ТекущаяЗапись) + 1, "ЧГ=0");
					КонецЕсли;	
				КонецЦикла;
			КонецЦикла;
			
			Если ПроверенныеСсылки.Количество() >= МаксимальноеКоличество Тогда
				ПроверенныеСсылки.Очистить();
			КонецЕсли;
			
			Если ПустаяСтрока(НомераПроблемныхЗаписей) Тогда
				Продолжить;
			КонецЕсли;
			
			Проблема = ОписаниеПроблемы(ОбщегоНазначения.ИдентификаторОбъектаМетаданных(ОбъектМетаданных), ПараметрыПроверки); // @skip-check query-in-loop - порционная проверка ведения учета
			
			Проблема.УточнениеПроблемы = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
				НСтр("ru = 'У регистра расчета ""%1"" в записях с номерами %2 по регистратору ""%3"" указаны ссылки на несуществующие данные.'"),
				ОбъектМетаданных.Представление(), НомераПроблемныхЗаписей, СтрокаРезультата.РегистраторРеквизитСсылка);
				
			ДополнительнаяИнформация = Новый Структура;
			ДополнительнаяИнформация.Вставить("Регистратор", СтрокаРезультата.РегистраторРеквизитСсылка);
			Проблема.ДополнительнаяИнформация = Новый ХранилищеЗначения(ДополнительнаяИнформация);
			
			ЗаписатьПроблему(Проблема, ПараметрыПроверки); // @skip-check query-in-loop - порционная проверка ведения учета
			
		КонецЦикла;
		
		Запрос.УстановитьПараметр("ДатаНачалаПроверки", СтрокаРезультата.Период);
		Результат = Запрос.Выполнить().Выгрузить(); // @skip-check query-in-loop - порционная проверка ведения учета
		
	КонецЦикла;
	
КонецПроцедуры

Функция СсылочныеРеквизитыРегистра(ОбъектМетаданных)
	
	Измерения = ОбъектМетаданных.Измерения;
	Реквизиты = ОбъектМетаданных.Реквизиты;
	
	Результат = Новый Массив;
	
	Для Каждого Реквизит Из Реквизиты Цикл
		Если СодержитСсылочныйТип(Реквизит) Тогда
			Результат.Добавить(Реквизит.Имя);
		КонецЕсли;
	КонецЦикла;
	
	Если ОбщегоНазначения.ЭтоРегистрРасчета(ОбъектМетаданных) Тогда
		
		Для Каждого Измерение Из Измерения Цикл
			Если СодержитСсылочныйТип(Измерение) Тогда
				Результат.Добавить(Измерение.Имя);
			КонецЕсли;
		КонецЦикла;
		
		СтандартныеРеквизиты = ОбъектМетаданных.СтандартныеРеквизиты;
		Для Каждого СтандартныйРеквизит Из СтандартныеРеквизиты Цикл
			Если СтандартныйРеквизит.Имя = "Регистратор" Или Не СодержитСсылочныйТип(СтандартныйРеквизит) Тогда
				Продолжить;
			КонецЕсли;
			Результат.Добавить(СтандартныйРеквизит.Имя);
		КонецЦикла;
		
	ИначеЕсли ОбщегоНазначения.ЭтоРегистрСведений(ОбъектМетаданных) Или ОбщегоНазначения.ЭтоРегистрНакопления(ОбъектМетаданных) Тогда
		
		Для Каждого Измерение Из Измерения Цикл
			Если СодержитСсылочныйТип(Измерение) Тогда
				Результат.Добавить(Измерение.Имя);
			КонецЕсли;
		КонецЦикла;
		
		Ресурсы = ОбъектМетаданных.Ресурсы;
		Для Каждого Ресурс Из Ресурсы Цикл
			Если СодержитСсылочныйТип(Ресурс) Тогда
				Результат.Добавить(Ресурс.Имя);
			КонецЕсли;
		КонецЦикла;
		
	КонецЕсли;
	
	Возврат Результат;
	
КонецФункции

Функция СодержитСсылочныйТип(Реквизит)
	
	ТипыРеквизита = Реквизит.Тип.Типы();
	Для Каждого ТекущийТип Из ТипыРеквизита Цикл
		Если ОбщегоНазначения.ЭтоСсылка(ТекущийТип) Тогда
			Возврат Истина;
		КонецЕсли;
	КонецЦикла;
	
	Возврат Ложь;
	
КонецФункции

Функция ВидыСубконто(ОбъектМетаданных)
	
	Если ОбъектМетаданных.ПланСчетов = Неопределено Тогда
		Возврат Новый Массив;
	КонецЕсли;
	
	ВидыСубконтоОМ = ОбъектМетаданных.ПланСчетов.ВидыСубконто;
	Если ВидыСубконтоОМ = Неопределено Или Не СодержитСсылочныйТип(ВидыСубконтоОМ) Тогда
		Возврат Новый Массив;
	КонецЕсли;
	
	Запрос = Новый Запрос(
	"ВЫБРАТЬ
	|	ПланВидовХарактеристик.Ссылка КАК ВидСубконто
	|ИЗ
	|	&ПланВидовХарактеристик КАК ПланВидовХарактеристик");
	
	Запрос.Текст = СтрЗаменить(Запрос.Текст, "&ПланВидовХарактеристик", ОбъектМетаданных.ПланСчетов.ВидыСубконто.ПолноеИмя());
	Возврат Запрос.Выполнить().Выгрузить().ВыгрузитьКолонку("ВидСубконто");
	
КонецФункции

Функция ИнформацияОНезависимомРегистре(ОбъектМетаданных, ПолучитьИзмерения = Истина, ПолучитьРесурсы = Истина, ПолучитьРеквизиты = Истина)
	
	ИнформацияОРегистре = Новый ТаблицаЗначений;
	ИнформацияОРегистре.Колонки.Добавить("ТипМетаданныхВИменительномПадеже", Новый ОписаниеТипов("Строка", , , , Новый КвалификаторыСтроки(16)));
	ИнформацияОРегистре.Колонки.Добавить("ТипМетаданныхВТворительномПадеже", Новый ОписаниеТипов("Строка", , , , Новый КвалификаторыСтроки(16)));
	ИнформацияОРегистре.Колонки.Добавить("ИмяМетаданных",                    Новый ОписаниеТипов("Строка", , , , Новый КвалификаторыСтроки(128)));
	
	ПоляВыборки = "";
	
	Если ПолучитьИзмерения Тогда
		Измерения = ОбъектМетаданных.Измерения;
		Для Каждого Измерение Из Измерения Цикл
			ИмяИзмерения = Измерение.Имя;
			ПоляВыборки  = ПоляВыборки + ?(ЗначениеЗаполнено(ПоляВыборки), ",", "") 
				+ "ПсевдонимЗаданнойТаблицы" + "." + ИмяИзмерения + " Как " + ИмяИзмерения + "ИзмерениеСсылка";
			ЗаполнитьЗначенияСвойств(ИнформацияОРегистре.Добавить(),
				Новый Структура("ТипМетаданныхВИменительномПадеже, ТипМетаданныхВТворительномПадеже, ИмяМетаданных", 
					"Измерение", НСтр("ru = 'измерении'"), ИмяИзмерения));
		КонецЦикла;
	КонецЕсли;
	
	Если ПолучитьРесурсы Тогда
		Ресурсы = ОбъектМетаданных.Ресурсы;
		Для Каждого Ресурс Из Ресурсы Цикл
			ИмяРесурса  = Ресурс.Имя;
			ПоляВыборки = ПоляВыборки + ?(ЗначениеЗаполнено(ПоляВыборки), ",", "")
				+ "ПсевдонимЗаданнойТаблицы" + "." + ИмяРесурса + " Как " + ИмяРесурса + "РесурсСсылка";
			ЗаполнитьЗначенияСвойств(ИнформацияОРегистре.Добавить(),
				Новый Структура("ТипМетаданныхВИменительномПадеже, ТипМетаданныхВТворительномПадеже, ИмяМетаданных", 
					"Ресурс", НСтр("ru = 'ресурсе'"), ИмяРесурса));
		КонецЦикла;
	КонецЕсли;
	
	Если ПолучитьРеквизиты Тогда
		Реквизиты = ОбъектМетаданных.Реквизиты;
		Для Каждого Реквизит Из Реквизиты Цикл
			ИмяРеквизита = Реквизит.Имя;
			ПоляВыборки = ПоляВыборки + ?(ЗначениеЗаполнено(ПоляВыборки), ",", "")
				+ "ПсевдонимЗаданнойТаблицы" + "." + ИмяРеквизита + " Как " + ИмяРеквизита + "РеквизитСсылка";
			ЗаполнитьЗначенияСвойств(ИнформацияОРегистре.Добавить(),
				Новый Структура("ТипМетаданныхВИменительномПадеже, ТипМетаданныхВТворительномПадеже, ИмяМетаданных", 
					"Реквизит", НСтр("ru = 'реквизите'"), ИмяРеквизита));
		КонецЦикла;
	КонецЕсли;
		
	Возврат Новый Структура("ИнформацияОРегистре, ПоляВыборки", ИнформацияОРегистре, ПоляВыборки);
	
КонецФункции

#КонецОбласти

#КонецОбласти

#Область ПроверкаЗаполненностиОбязательныхРеквизитов


// Параметры:
//   ОбъектМетаданных - Произвольный
//   ПараметрыПроверки - см. КонтрольВеденияУчета.ОписаниеПроблемы
//
Процедура НайтиНезаполненныеОбязательныеРеквизиты(ОбъектМетаданных, ПараметрыПроверки)
	
	МодульОценкаПроизводительности = Неопределено;
	Если ОбщегоНазначения.ПодсистемаСуществует("СтандартныеПодсистемы.ОценкаПроизводительности") Тогда
		МодульОценкаПроизводительности = ОбщегоНазначения.ОбщийМодуль("ОценкаПроизводительности");
	КонецЕсли;
	
	ПолноеИмя = ОбъектМетаданных.ПолноеИмя();
	Реквизиты = ОбъектМетаданных.Реквизиты;
	
	Запрос = Новый Запрос;
	Запрос.УстановитьПараметр("Ссылка", "");
	
	ТекстЗапроса = 
	"ВЫБРАТЬ ПЕРВЫЕ 1000
	|	ПсевдонимЗаданнойТаблицы.Ссылка КАК ПроблемныйОбъект
	|ИЗ
	|	&ОбъектМетаданных КАК ПсевдонимЗаданнойТаблицы
	|ГДЕ
	|	&Условие
	|	И НЕ ПсевдонимЗаданнойТаблицы.ПометкаУдаления
	|
	|УПОРЯДОЧИТЬ ПО
	|	ПсевдонимЗаданнойТаблицы.Ссылка";
	
	ИмяТаблицы = СтрЗаменить(ПолноеИмя, ".", "");
	Если ПараметрыПроверки.Свойство("ПроверяемыеОбъекты") И ПараметрыПроверки.ПроверяемыеОбъекты <> Неопределено Тогда
		Условие = ИмяТаблицы + ".Ссылка В (&Ссылка)";
		Запрос.УстановитьПараметр("Ссылка", ПараметрыПроверки.ПроверяемыеОбъекты);
	Иначе
		Условие = ИмяТаблицы + ".Ссылка > &Ссылка";
		Запрос.УстановитьПараметр("Ссылка", "");
	КонецЕсли;
	
	ТекстЗапроса = СтрЗаменить(ТекстЗапроса, "ПсевдонимЗаданнойТаблицы", ИмяТаблицы);
	ТекстЗапроса = СтрЗаменить(ТекстЗапроса, "&Условие", Условие);
	ТекстЗапроса = СтрЗаменить(ТекстЗапроса, "&ОбъектМетаданных", ПолноеИмя);
	
	Запрос.Текст = ТекстЗапроса;
	Результат = Запрос.Выполнить().Выгрузить();
	
	ОбщееКоличествоДанных = 0;
	ОграничениеПоДате     = ПараметрыПроверки.ДатаНачалаПроверки;
	Пока Результат.Количество() > 0 Цикл
		
		Если МодульОценкаПроизводительности <> Неопределено Тогда
			ОписаниеЗамера = МодульОценкаПроизводительности.НачатьЗамерДлительнойОперации(ПараметрыПроверки.Идентификатор + "." + ПолноеИмя);
		КонецЕсли;
		
		Для Каждого СтрокаРезультата Из Результат Цикл
			
			СсылкаНаОбъект = СтрокаРезультата.ПроблемныйОбъект;
			
			Если ЗначениеЗаполнено(ОграничениеПоДате)
				И ОбщегоНазначения.ЭтоДокумент(ОбъектМетаданных)
				И ОбщегоНазначения.ЗначениеРеквизитаОбъекта(СсылкаНаОбъект, "Дата") < ОграничениеПоДате Тогда
				Продолжить;
			КонецЕсли;
			
			Если ОбщегоНазначения.ЭтоДокумент(ОбъектМетаданных) И Не ОбщегоНазначения.ЗначениеРеквизитаОбъекта(СсылкаНаОбъект, "Проведен") Тогда
				Продолжить;
			КонецЕсли;
			
			Если ОбщегоНазначения.ЭтоПланОбмена(ОбъектМетаданных) И СсылкаНаОбъект.ЭтотУзел Тогда
				Продолжить;
			КонецЕсли;
			
			ПроверяемыйОбъект = СсылкаНаОбъект.ПолучитьОбъект();
			Если ПроверяемыйОбъект.ПроверитьЗаполнение() Тогда
				Продолжить;
			КонецЕсли;
			
			Проблема = ОписаниеПроблемы(СсылкаНаОбъект, ПараметрыПроверки); // @skip-check query-in-loop - порционная проверка ведения учета
			
			Проблема.УточнениеПроблемы = НСтр("ru = 'Не заполнены реквизиты, обязательные к заполнению:'") + Символы.ПС + ОшибкиЗаполненияОбъекта();
			Если Реквизиты.Найти("Ответственный") <> Неопределено Тогда
				Проблема.Вставить("Ответственный", ОбщегоНазначения.ЗначениеРеквизитаОбъекта(СсылкаНаОбъект, "Ответственный"));
			КонецЕсли;
			
			ЗаписатьПроблему(Проблема, ПараметрыПроверки); // @skip-check query-in-loop - порционная проверка ведения учета
			
		КонецЦикла;
		
		ОбщееКоличествоДанных = ОбщееКоличествоДанных + Результат.Количество();
		Если МодульОценкаПроизводительности <> Неопределено Тогда
			МодульОценкаПроизводительности.ЗакончитьЗамерДлительнойОперации(ОписаниеЗамера, ОбщееКоличествоДанных);
		КонецЕсли;
		
		Если ПараметрыПроверки.Свойство("ПроверяемыеОбъекты") И ПараметрыПроверки.ПроверяемыеОбъекты <> Неопределено Тогда
			Прервать;
		КонецЕсли;
		
		Запрос.УстановитьПараметр("Ссылка", СтрокаРезультата.ПроблемныйОбъект);
		Результат = Запрос.Выполнить().Выгрузить(); // @skip-check query-in-loop - порционная проверка ведения учета
		
	КонецЦикла;
	
КонецПроцедуры

Процедура НайтиНезаполненныеОбязательныеРеквизитыВРегистрах(ОбъектМетаданных, ПараметрыПроверки)
	
	Если ОбъектМетаданных.Измерения.Количество() = 0 Тогда
		Возврат;
	КонецЕсли;
	
	Если ОбщегоНазначения.ЭтоРегистрСведений(ОбъектМетаданных) Тогда
		
		Если ОбъектМетаданных.РежимЗаписи = Метаданные.СвойстваОбъектов.РежимЗаписиРегистра.ПодчинениеРегистратору Тогда
			Если ОбъектМетаданных.ПериодичностьРегистраСведений <> Метаданные.СвойстваОбъектов.ПериодичностьРегистраСведений.Непериодический Тогда
				НайтиНезаполненныеОбязательныеРеквизитыВПодчиненныхПериодическихРегистрах(ОбъектМетаданных, ПараметрыПроверки);
			Иначе
				НайтиНезаполненныеОбязательныеРеквизитыВПодчиненныхНепериодическихРегистрах(ОбъектМетаданных, ПараметрыПроверки);
			КонецЕсли;
		Иначе	
			Если ОбъектМетаданных.ПериодичностьРегистраСведений <> Метаданные.СвойстваОбъектов.ПериодичностьРегистраСведений.Непериодический Тогда
				НайтиНезаполненныеОбязательныеРеквизитыВНезависимыхПериодическихРегистрахСведений(ОбъектМетаданных, ПараметрыПроверки);
			Иначе
				НайтиНезаполненныеОбязательныеРеквизитыВНезависимыхНепериодическихРегистрахСведений(ОбъектМетаданных, ПараметрыПроверки);
			КонецЕсли;
		КонецЕсли;
		
	ИначеЕсли ОбщегоНазначения.ЭтоРегистрНакопления(ОбъектМетаданных)
		Или ОбщегоНазначения.ЭтоРегистрБухгалтерии(ОбъектМетаданных)
		Или ОбщегоНазначения.ЭтоРегистрРасчета(ОбъектМетаданных) Тогда
		
		НайтиНезаполненныеОбязательныеРеквизитыВПодчиненныхНепериодическихРегистрах(ОбъектМетаданных, ПараметрыПроверки);
		
	КонецЕсли;
	
КонецПроцедуры

// Параметры:
//   ОбъектМетаданных - ОбъектМетаданныхРегистрБухгалтерии
//                    - Произвольный
//                    - ОбъектМетаданныхРегистрСведений
//                    - ОбъектМетаданныхРегистрНакопления
//                    - ОбъектМетаданныхРегистрРасчета
//   ПараметрыПроверки - см. КонтрольВеденияУчета.ОписаниеПроблемы 
//
Процедура НайтиНезаполненныеОбязательныеРеквизитыВПодчиненныхПериодическихРегистрах(ОбъектМетаданных, ПараметрыПроверки)
	
	МодульОценкаПроизводительности = Неопределено;
	Если ОбщегоНазначения.ПодсистемаСуществует("СтандартныеПодсистемы.ОценкаПроизводительности") Тогда
		МодульОценкаПроизводительности = ОбщегоНазначения.ОбщийМодуль("ОценкаПроизводительности");
	КонецЕсли;
	
	ПолноеИмя = ОбъектМетаданных.ПолноеИмя();
	ИмяТаблицы = СтрЗаменить(ПолноеИмя, ".", "");
	Если ОбщегоНазначения.ЭтоРегистрРасчета(ОбъектМетаданных) Тогда
		
		Запрос = Новый Запрос(
		"ВЫБРАТЬ ПЕРВЫЕ 1000
		|	ПсевдонимЗаданнойТаблицы.Регистратор КАК РегистраторРеквизитСсылка,
		|	ПсевдонимЗаданнойТаблицы.ПериодРегистрации КАК Период
		|ИЗ
		|	&ОбъектМетаданных КАК ПсевдонимЗаданнойТаблицы
		|ГДЕ
		|	ПсевдонимЗаданнойТаблицы.ПериодРегистрации > &ДатаНачалаПроверки
		|
		|СГРУППИРОВАТЬ ПО
		|	ПсевдонимЗаданнойТаблицы.ПериодРегистрации,
		|	ПсевдонимЗаданнойТаблицы.Регистратор
		|
		|УПОРЯДОЧИТЬ ПО
		|	ПсевдонимЗаданнойТаблицы.ПериодРегистрации");
		
		Запрос.Текст = СтрЗаменить(Запрос.Текст, "ПсевдонимЗаданнойТаблицы", ИмяТаблицы);
		Запрос.Текст = СтрЗаменить(Запрос.Текст, "&ОбъектМетаданных", ПолноеИмя);
		Запрос.УстановитьПараметр("ДатаНачалаПроверки", ПараметрыПроверки.ДатаНачалаПроверки);
		
	ИначеЕсли ОбщегоНазначения.ЭтоРегистрБухгалтерии(ОбъектМетаданных) Тогда
		
		Запрос = Новый Запрос(
		"ВЫБРАТЬ
		|	ПсевдонимЗаданнойТаблицы.Регистратор КАК РегистраторРеквизитСсылка,
		|	ПсевдонимЗаданнойТаблицы.Период КАК Период
		|ИЗ
		|	&ОбъектМетаданных КАК ПсевдонимЗаданнойТаблицы
		|
		|СГРУППИРОВАТЬ ПО
		|	ПсевдонимЗаданнойТаблицы.Период,
		|	ПсевдонимЗаданнойТаблицы.Регистратор
		|
		|УПОРЯДОЧИТЬ ПО
		|	ПсевдонимЗаданнойТаблицы.Период");
		
		Запрос.Текст = СтрЗаменить(Запрос.Текст, "ПсевдонимЗаданнойТаблицы", ИмяТаблицы);
		Запрос.Текст = СтрЗаменить(Запрос.Текст, "&ОбъектМетаданных", 
			ПолноеИмя + ".ДвиженияССубконто(, , Период > &ДатаНачалаПроверки И Регистратор > &Регистратор, , 1000)");
		Запрос.УстановитьПараметр("ДатаНачалаПроверки", ПараметрыПроверки.ДатаНачалаПроверки);
		
	Иначе
		
		Запрос = Новый Запрос(
		"ВЫБРАТЬ ПЕРВЫЕ 1000
		|	ПсевдонимЗаданнойТаблицы.Регистратор КАК РегистраторРеквизитСсылка,
		|	ПсевдонимЗаданнойТаблицы.Период КАК Период
		|ИЗ
		|	&ОбъектМетаданных КАК ПсевдонимЗаданнойТаблицы
		|ГДЕ
		|	ПсевдонимЗаданнойТаблицы.Период > &ДатаНачалаПроверки
		|
		|СГРУППИРОВАТЬ ПО
		|	ПсевдонимЗаданнойТаблицы.Период,
		|	ПсевдонимЗаданнойТаблицы.Регистратор
		|
		|УПОРЯДОЧИТЬ ПО
		|	ПсевдонимЗаданнойТаблицы.Период");
		
		Запрос.Текст = СтрЗаменить(Запрос.Текст, "ПсевдонимЗаданнойТаблицы", ИмяТаблицы);
		Запрос.Текст = СтрЗаменить(Запрос.Текст, "&ОбъектМетаданных", ПолноеИмя);
		Запрос.УстановитьПараметр("ДатаНачалаПроверки", ПараметрыПроверки.ДатаНачалаПроверки);
		
	КонецЕсли;
	
	Результат        = Запрос.Выполнить().Выгрузить();
	МенеджерРегистра = ОбщегоНазначения.МенеджерОбъектаПоПолномуИмени(ПолноеИмя);
	ОбщееКоличествоДанных = 0;
	
	Пока Результат.Количество() > 0 Цикл
		
		Если МодульОценкаПроизводительности <> Неопределено Тогда
			ОписаниеЗамера = МодульОценкаПроизводительности.НачатьЗамерДлительнойОперации(ПараметрыПроверки.Идентификатор + "." + ПолноеИмя);
		КонецЕсли;
		
		Для Каждого СтрокаРезультата Из Результат Цикл
			
			ТекущийНаборЗаписей = МенеджерРегистра.СоздатьНаборЗаписей();
			ТекущийНаборЗаписей.Отбор.Регистратор.Установить(СтрокаРезультата.РегистраторРеквизитСсылка);
			ТекущийНаборЗаписей.Прочитать();
			
			Если ТекущийНаборЗаписей.ПроверитьЗаполнение() Тогда
				Продолжить;
			КонецЕсли;
			
			ДополнительнаяИнформация = Новый Структура;
			ДополнительнаяИнформация.Вставить("Регистратор", СтрокаРезультата.РегистраторРеквизитСсылка);
			
			Проблема = ОписаниеПроблемы(ОбщегоНазначения.ИдентификаторОбъектаМетаданных(ОбъектМетаданных), ПараметрыПроверки); // @skip-check query-in-loop - порционная проверка ведения учета
			
			Проблема.УточнениеПроблемы = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(НСтр("ru = 'У записи с полями:
				|%1,
				|обнаружены незаполненные данные, обязательные к заполнению: %2'"),
				" • " + НСтр("ru = 'Регистратор:'") + " = """ + СтрокаРезультата.РегистраторРеквизитСсылка, Символы.ПС + ОшибкиЗаполненияОбъекта());
			Проблема.ДополнительнаяИнформация = Новый ХранилищеЗначения(ДополнительнаяИнформация);
			
			ЗаписатьПроблему(Проблема, ПараметрыПроверки); // @skip-check query-in-loop - порционная проверка ведения учета
			
		КонецЦикла;
		
		ОбщееКоличествоДанных = ОбщееКоличествоДанных + Результат.Количество();
		
		Если МодульОценкаПроизводительности <> Неопределено Тогда
			МодульОценкаПроизводительности.ЗакончитьЗамерДлительнойОперации(ОписаниеЗамера, ОбщееКоличествоДанных);
		КонецЕсли;
		
		Запрос.УстановитьПараметр("ДатаНачалаПроверки", СтрокаРезультата.Период);
		Результат = Запрос.Выполнить().Выгрузить(); // @skip-check query-in-loop - порционная проверка ведения учета
		
	КонецЦикла;
	
КонецПроцедуры


// Параметры:
//   ОбъектМетаданных - ОбъектМетаданныхРегистрБухгалтерии
//                    - Произвольный
//                    - ОбъектМетаданныхРегистрСведений
//                    - ОбъектМетаданныхРегистрНакопления
//                    - ОбъектМетаданныхРегистрРасчета
//   ПараметрыПроверки - см. КонтрольВеденияУчета.ОписаниеПроблемы
//
Процедура НайтиНезаполненныеОбязательныеРеквизитыВПодчиненныхНепериодическихРегистрах(ОбъектМетаданных, ПараметрыПроверки)
	
	МодульОценкаПроизводительности = Неопределено;
	Если ОбщегоНазначения.ПодсистемаСуществует("СтандартныеПодсистемы.ОценкаПроизводительности") Тогда
		МодульОценкаПроизводительности = ОбщегоНазначения.ОбщийМодуль("ОценкаПроизводительности");
	КонецЕсли;
	
	ПолноеИмя = ОбъектМетаданных.ПолноеИмя();
	
	Запрос = Новый Запрос(
	"ВЫБРАТЬ ПЕРВЫЕ 1000
	|	ПсевдонимЗаданнойТаблицы.Регистратор КАК РегистраторРеквизитСсылка
	|ИЗ
	|	&ОбъектМетаданных КАК ПсевдонимЗаданнойТаблицы
	|ГДЕ
	|	ПсевдонимЗаданнойТаблицы.Регистратор > &Регистратор
	|
	|СГРУППИРОВАТЬ ПО
	|	ПсевдонимЗаданнойТаблицы.Регистратор
	|
	|УПОРЯДОЧИТЬ ПО
	|	ПсевдонимЗаданнойТаблицы.Регистратор");
	
	ИмяТаблицы = СтрЗаменить(ПолноеИмя, ".", "");
	Запрос.Текст = СтрЗаменить(Запрос.Текст, "ПсевдонимЗаданнойТаблицы", ИмяТаблицы);
	Запрос.Текст = СтрЗаменить(Запрос.Текст, "&ОбъектМетаданных", ПолноеИмя);
	Запрос.УстановитьПараметр("Регистратор", "");
	
	Результат        = Запрос.Выполнить().Выгрузить();
	МенеджерРегистра = ОбщегоНазначения.МенеджерОбъектаПоПолномуИмени(ПолноеИмя);
	
	ОбщееКоличествоДанных = 0;
	Пока Результат.Количество() > 0 Цикл
		
		Если МодульОценкаПроизводительности <> Неопределено Тогда
			ОписаниеЗамера = МодульОценкаПроизводительности.НачатьЗамерДлительнойОперации(ПараметрыПроверки.Идентификатор + "." + ПолноеИмя);
		КонецЕсли;
		Для Каждого СтрокаРезультата Из Результат Цикл
			
			ТекущийНаборЗаписей = МенеджерРегистра.СоздатьНаборЗаписей();
			ТекущийНаборЗаписей.Отбор.Регистратор.Установить(СтрокаРезультата.РегистраторРеквизитСсылка);
			ТекущийНаборЗаписей.Прочитать();
			
			Если ТекущийНаборЗаписей.ПроверитьЗаполнение() Тогда
				Продолжить;
			КонецЕсли;
			
			ДополнительнаяИнформация = Новый Структура;
			ДополнительнаяИнформация.Вставить("Регистратор", СтрокаРезультата.РегистраторРеквизитСсылка);
			
			Проблема = ОписаниеПроблемы(ОбщегоНазначения.ИдентификаторОбъектаМетаданных(ОбъектМетаданных), ПараметрыПроверки); // @skip-check query-in-loop - порционная проверка ведения учета
			
			Проблема.УточнениеПроблемы = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(НСтр("ru = 'У записи с полями:
				|%1,
				|обнаружены незаполненные данные, обязательные к заполнению: %2'"),
				" • " + НСтр("ru = 'Регистратор:'") + " = """ + СтрокаРезультата.РегистраторРеквизитСсылка, Символы.ПС + ОшибкиЗаполненияОбъекта());
			Проблема.ДополнительнаяИнформация = Новый ХранилищеЗначения(ДополнительнаяИнформация);
			
			ЗаписатьПроблему(Проблема, ПараметрыПроверки); // @skip-check query-in-loop - порционная проверка ведения учета
			
		КонецЦикла;
		
		ОбщееКоличествоДанных = ОбщееКоличествоДанных + Результат.Количество();
		Если МодульОценкаПроизводительности <> Неопределено Тогда
			МодульОценкаПроизводительности.ЗакончитьЗамерДлительнойОперации(ОписаниеЗамера, ОбщееКоличествоДанных);
		КонецЕсли;
		
		Запрос.УстановитьПараметр("Регистратор", СтрокаРезультата.РегистраторРеквизитСсылка);
		Результат = Запрос.Выполнить().Выгрузить(); // @skip-check query-in-loop - порционная проверка ведения учета
		
	КонецЦикла;
	
КонецПроцедуры


// Параметры:
//   ОбъектМетаданных - ОбъектМетаданныхРегистрБухгалтерии
//                    - Произвольный
//                    - ОбъектМетаданныхРегистрСведений
//                    - ОбъектМетаданныхРегистрНакопления
//                    - ОбъектМетаданныхРегистрРасчета
//   ПараметрыПроверки - см. КонтрольВеденияУчета.ОписаниеПроблемы
//
Процедура НайтиНезаполненныеОбязательныеРеквизитыВНезависимыхНепериодическихРегистрахСведений(ОбъектМетаданных, ПараметрыПроверки)
	
	МодульОценкаПроизводительности = Неопределено;
	Если ОбщегоНазначения.ПодсистемаСуществует("СтандартныеПодсистемы.ОценкаПроизводительности") Тогда
		МодульОценкаПроизводительности = ОбщегоНазначения.ОбщийМодуль("ОценкаПроизводительности");
	КонецЕсли;
	
	ПолноеИмя           = ОбъектМетаданных.ПолноеИмя();
	МенеджерРегистра    = ОбщегоНазначения.МенеджерОбъектаПоПолномуИмени(ПолноеИмя);
	УсловиеПоИзмерениям = "";
	ПоляУпорядочивания  = "";
	Измерения           = ОбъектМетаданных.Измерения;
	
	ЕстьОбязательныеРеквизиты = ЕстьОбязательныеРеквизиты(ОбъектМетаданных);
	Если Не ЕстьОбязательныеРеквизиты Тогда
		Возврат;
	КонецЕсли;
	
	ИмяТаблицы = СтрЗаменить(ПолноеИмя, ".", "");
	
	Для Каждого Измерение Из Измерения Цикл
		УсловиеПоИзмерениям = УсловиеПоИзмерениям + ?(ЗначениеЗаполнено(УсловиеПоИзмерениям), " И ", "") + ИмяТаблицы + "." + Измерение.Имя + " >= &" + Измерение.Имя;
		ПоляУпорядочивания  = ПоляУпорядочивания + ?(ЗначениеЗаполнено(ПоляУпорядочивания), ", ", "") + ИмяТаблицы + "." + Измерение.Имя;
	КонецЦикла;
	
	ПоляВыборки = ИнформацияОНезависимомРегистре(ОбъектМетаданных, Истина, Ложь, Ложь).ПоляВыборки;
	
	ТекстЗапроса =
	"ВЫБРАТЬ ПЕРВЫЕ 1000
	|	&ПоляВыборки КАК ПоляВыборки
	|ИЗ
	|	&ОбъектМетаданных КАК ПсевдонимЗаданнойТаблицы
	|ГДЕ
	|	&Условие
	|
	|УПОРЯДОЧИТЬ ПО
	|	&ПоляУпорядочивания";
	
	ТекстЗапроса = СтрЗаменить(ТекстЗапроса, "&ПоляУпорядочивания", ПоляУпорядочивания);
	ТекстЗапроса = СтрЗаменить(ТекстЗапроса, "&ПоляВыборки КАК ПоляВыборки", ПоляВыборки);
	ТекстЗапроса = СтрЗаменить(ТекстЗапроса, "&ОбъектМетаданных", ОбъектМетаданных.ПолноеИмя());
	ТекстЗапроса = СтрЗаменить(ТекстЗапроса, "ПсевдонимЗаданнойТаблицы", ИмяТаблицы);
	
	ТекстПервогоЗапроса   = СтрЗаменить(ТекстЗапроса, "&Условие", "Истина");
	ТекстЗапросаСУсловием = СтрЗаменить(ТекстЗапроса, "&Условие", УсловиеПоИзмерениям);
	
	Запрос = Новый Запрос(ТекстПервогоЗапроса);
	Результат = Запрос.Выполнить().Выгрузить();
	
	ЭтоПервыйПроход = Истина;
	ОбщееКоличествоДанных = 0;
	Пока Результат.Количество() > 0 Цикл
		
		// Последняя запись уже проверена на предыдущей итерации.
		Если Не ЭтоПервыйПроход И Результат.Количество() = 1 Тогда
			Прервать;
		КонецЕсли;
		
		Если МодульОценкаПроизводительности <> Неопределено Тогда
			ОписаниеЗамера = МодульОценкаПроизводительности.НачатьЗамерДлительнойОперации(ПараметрыПроверки.Идентификатор + "." + ПолноеИмя);
		КонецЕсли;
		
		Для Каждого СтрокаРезультата Из Результат Цикл
			
			Если Не ЭтоПервыйПроход И Результат.Индекс(СтрокаРезультата) = 0 Тогда
				Продолжить;
			КонецЕсли;
			
			ТекущийНаборЗаписей = МенеджерРегистра.СоздатьНаборЗаписей();// РегистрСведенийНаборЗаписей
			ОтборТекущегоНабора = ТекущийНаборЗаписей.Отбор;
			
			ПредставлениеОтбораНабораЗаписей = "";
			Для Каждого Измерение Из Измерения Цикл
				
				ИмяИзмерения      = Измерение.Имя;
				ЗначениеИзмерения = СтрокаРезультата[ИмяИзмерения + "ИзмерениеСсылка"];
				Если Не ЗначениеЗаполнено(ЗначениеИзмерения) Тогда
					Продолжить;
				КонецЕсли;
				
				ОтборПоИзмерению = ОтборТекущегоНабора[ИмяИзмерения];// ЭлементОтбора
				ОтборПоИзмерению.Установить(ЗначениеИзмерения);
				
				ПредставлениеОтбораНабораЗаписей = ПредставлениеОтбораНабораЗаписей + ?(ЗначениеЗаполнено(ПредставлениеОтбораНабораЗаписей), Символы.ПС, "")
					+ " • " + ИмяИзмерения + " = """ + СтрокаРезультата[ИмяИзмерения + "ИзмерениеСсылка"] + """";
				
			КонецЦикла;
			ТекущийНаборЗаписей.Прочитать();
			
			Если ТекущийНаборЗаписей.ПроверитьЗаполнение() Тогда
				Продолжить;
			КонецЕсли;
			
			ДополнительнаяИнформация = Новый Структура;
			Для Каждого Измерение Из Измерения Цикл
				ИзмерениеСсылка = СтрокаРезультата[Измерение.Имя + "ИзмерениеСсылка"];
				ДополнительнаяИнформация.Вставить(Измерение.Имя, ИзмерениеСсылка);
			КонецЦикла;
			
			Проблема = ОписаниеПроблемы(ОбщегоНазначения.ИдентификаторОбъектаМетаданных(ОбъектМетаданных), ПараметрыПроверки); // @skip-check query-in-loop - порционная проверка ведения учета
			
			Проблема.УточнениеПроблемы        = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(НСтр("ru = 'У записи с полями:
				|%1,
				|обнаружены незаполненные данные, обязательные к заполнению: %2'"), ПредставлениеОтбораНабораЗаписей, Символы.ПС + ОшибкиЗаполненияОбъекта());
			Проблема.ДополнительнаяИнформация = Новый ХранилищеЗначения(ДополнительнаяИнформация);
			
			ЗаписатьПроблему(Проблема, ПараметрыПроверки); // @skip-check query-in-loop - порционная проверка ведения учета
			
		КонецЦикла;
		
		Если ЭтоПервыйПроход Тогда
			ЭтоПервыйПроход = Ложь;
			Запрос.Текст = ТекстЗапросаСУсловием;
		КонецЕсли;
		
		Для Каждого Измерение Из Измерения Цикл
			Запрос.УстановитьПараметр(Измерение.Имя, СтрокаРезультата[Измерение.Имя + "ИзмерениеСсылка"]);
		КонецЦикла;
		
		ОбщееКоличествоДанных = ОбщееКоличествоДанных + Результат.Количество();
		Если МодульОценкаПроизводительности <> Неопределено Тогда
			МодульОценкаПроизводительности.ЗакончитьЗамерДлительнойОперации(ОписаниеЗамера, ОбщееКоличествоДанных);
		КонецЕсли;
		
		Результат = Запрос.Выполнить().Выгрузить(); // @skip-check query-in-loop - порционная проверка ведения учета
		
	КонецЦикла;
	
КонецПроцедуры

// Параметры:
//   ОбъектМетаданных - ОбъектМетаданныхРегистрБухгалтерии
//                    - Произвольный
//                    - ОбъектМетаданныхРегистрСведений
//                    - ОбъектМетаданныхРегистрНакопления
//                    - ОбъектМетаданныхРегистрРасчета
//   ПараметрыПроверки - см. КонтрольВеденияУчета.ОписаниеПроблемы
//
Процедура НайтиНезаполненныеОбязательныеРеквизитыВНезависимыхПериодическихРегистрахСведений(ОбъектМетаданных, ПараметрыПроверки)
	
	МодульОценкаПроизводительности = Неопределено;
	Если ОбщегоНазначения.ПодсистемаСуществует("СтандартныеПодсистемы.ОценкаПроизводительности") Тогда
		МодульОценкаПроизводительности = ОбщегоНазначения.ОбщийМодуль("ОценкаПроизводительности");
	КонецЕсли;
	
	ПолноеИмя          = ОбъектМетаданных.ПолноеИмя();
	ИмяТаблицы         = СтрЗаменить(ПолноеИмя, ".", "");
	МенеджерРегистра   = ОбщегоНазначения.МенеджерОбъектаПоПолномуИмени(ПолноеИмя);
	УсловиеПоИзмерениям = "";
	ПоляУпорядочивания = ИмяТаблицы + "." + "Период";
	Измерения          = ОбъектМетаданных.Измерения;
	
	Для Каждого Измерение Из Измерения Цикл
		УсловиеПоИзмерениям = УсловиеПоИзмерениям + ?(ЗначениеЗаполнено(УсловиеПоИзмерениям), " И ", "") + ИмяТаблицы + "." + Измерение.Имя + " >= &" + Измерение.Имя;
		ПоляУпорядочивания = ПоляУпорядочивания + ?(ЗначениеЗаполнено(ПоляУпорядочивания), ", ", "") + ИмяТаблицы + "." + Измерение.Имя;
	КонецЦикла;
	
	ПоляВыборки = ИнформацияОНезависимомРегистре(ОбъектМетаданных, Истина, Ложь, Ложь).ПоляВыборки;
	
	ТекстЗапроса =
		"ВЫБРАТЬ ПЕРВЫЕ 1000
		|	ПсевдонимЗаданнойТаблицы.Период КАК Период,
		|	&ПоляВыборки КАК ПоляВыборки
		|ИЗ
		|	&ОбъектМетаданных КАК ПсевдонимЗаданнойТаблицы
		|ГДЕ
		|	(ПсевдонимЗаданнойТаблицы.Период > &Период
		|				И НЕ &ТолькоУказанныйПериод
		|			ИЛИ ПсевдонимЗаданнойТаблицы.Период = &Период
		|				И &ТолькоУказанныйПериод)
		|	И &Условие
		|УПОРЯДОЧИТЬ ПО &ПоляУпорядочивания";
	
	ТекстЗапроса = СтрЗаменить(ТекстЗапроса, "&ПоляУпорядочивания", ПоляУпорядочивания);
	ТекстЗапроса = СтрЗаменить(ТекстЗапроса, "&ПоляВыборки КАК ПоляВыборки", ПоляВыборки);
	ТекстЗапроса = СтрЗаменить(ТекстЗапроса, "&ОбъектМетаданных", ОбъектМетаданных.ПолноеИмя());
	ТекстЗапроса = СтрЗаменить(ТекстЗапроса, "ПсевдонимЗаданнойТаблицы", ИмяТаблицы);
	
	ТекстПервогоЗапроса = СтрЗаменить(ТекстЗапроса, "&Условие", "Истина");
	ТекстЗапросаСУсловием = СтрЗаменить(ТекстЗапроса, "&Условие", УсловиеПоИзмерениям);
	
	Запрос = Новый Запрос(ТекстПервогоЗапроса);
	Запрос.УстановитьПараметр("Период", ПараметрыПроверки.ДатаНачалаПроверки);
	Запрос.УстановитьПараметр("ТолькоУказанныйПериод", Ложь);
	
	Результат = Запрос.Выполнить().Выгрузить();
	ЭтоПервыйПроход = Истина;
	
	ОбщееКоличествоДанных = Результат.Количество();
	Пока Результат.Количество() > 0 Цикл
		
		// Последняя запись уже проверена на предыдущей итерации.
		Если Не ЭтоПервыйПроход И Результат.Количество() = 1 Тогда
			Прервать;
		КонецЕсли;
		//
		Если МодульОценкаПроизводительности <> Неопределено Тогда
			ОписаниеЗамера = МодульОценкаПроизводительности.НачатьЗамерДлительнойОперации(ПараметрыПроверки.Идентификатор + "." + ПолноеИмя);
		КонецЕсли;
		//
		Для Каждого СтрокаРезультата Из Результат Цикл
			
			ТекущийНаборЗаписей = МенеджерРегистра.СоздатьНаборЗаписей();
			ОтборТекущегоНабора = ТекущийНаборЗаписей.Отбор;
			
			ПредставлениеОтбораНабораЗаписей = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(" • %1 = ""%2""",
				НСтр("ru = 'Период'"), СтрокаРезультата.Период);
			Для Каждого Измерение Из Измерения Цикл
				
				ИмяИзмерения      = Измерение.Имя;
				ЗначениеИзмерения = СтрокаРезультата[ИмяИзмерения + "ИзмерениеСсылка"];
				Если Не ЗначениеЗаполнено(ЗначениеИзмерения) Тогда
					Продолжить;
				КонецЕсли;
				
				ОтборПоИзмерению = ОтборТекущегоНабора[ИмяИзмерения];// ЭлементОтбора
				ОтборПоИзмерению.Установить(ЗначениеИзмерения);
				
				ПредставлениеОтбораНабораЗаписей = ПредставлениеОтбораНабораЗаписей + Символы.ПС
					+ " • " + ИмяИзмерения + " = """ + СтрокаРезультата[ИмяИзмерения + "ИзмерениеСсылка"] + """";
				
			КонецЦикла;
			ТекущийНаборЗаписей.Прочитать();
			
			Если ТекущийНаборЗаписей.ПроверитьЗаполнение() Тогда
				Продолжить;
			КонецЕсли;
			
			УточнениеПроблемы = ОшибкиЗаполненияОбъекта();
			
			СтруктураНабораЗаписей = Новый Структура;
			СтруктураНабораЗаписей.Вставить("Период", СтрокаРезультата.Период);
			Для Каждого Измерение Из Измерения Цикл
				ИзмерениеСсылка = СтрокаРезультата[Измерение.Имя + "ИзмерениеСсылка"];
				СтруктураНабораЗаписей.Вставить(Измерение.Имя, ИзмерениеСсылка);
			КонецЦикла;
			
			Проблема = ОписаниеПроблемы(ОбщегоНазначения.ИдентификаторОбъектаМетаданных(ОбъектМетаданных), ПараметрыПроверки); // @skip-check query-in-loop - порционная проверка ведения учета
			
			Проблема.УточнениеПроблемы        = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(НСтр("ru = 'У записи с полями:
				|%1,
				|обнаружены незаполненные данные, обязательные к заполнению: %2'"), ПредставлениеОтбораНабораЗаписей, Символы.ПС + УточнениеПроблемы);
			Проблема.ДополнительнаяИнформация = Новый ХранилищеЗначения(СтруктураНабораЗаписей);
			ЗаписатьПроблему(Проблема, ПараметрыПроверки); // @skip-check query-in-loop - порционная проверка ведения учета
			
		КонецЦикла;
		
		Если ЭтоПервыйПроход Тогда
			ЭтоПервыйПроход = Ложь;
		КонецЕсли;
		
		// После прохода по всем периодам делаем дообработку по последнему периоду.
		Запрос.Текст = ТекстЗапросаСУсловием;
		Запрос.УстановитьПараметр("Период", СтрокаРезультата.Период);
		Запрос.УстановитьПараметр("ТолькоУказанныйПериод", Истина);
		Для Каждого Измерение Из Измерения Цикл
			Запрос.УстановитьПараметр(Измерение.Имя, СтрокаРезультата[Измерение.Имя + "ИзмерениеСсылка"]);
		КонецЦикла;
		Результат = Запрос.Выполнить().Выгрузить(); // @skip-check query-in-loop - порционная проверка ведения учета
		// Если по последнему периоду все записи обработаны, то выбираем все записи по следующим периодам.
		Если Результат.Количество() = 0 Или Результат.Количество() = 1 Тогда
			Запрос.Текст = ТекстПервогоЗапроса;
			Запрос.УстановитьПараметр("Период", СтрокаРезультата.Период);
			Запрос.УстановитьПараметр("ТолькоУказанныйПериод", Ложь);
			Результат = Запрос.Выполнить().Выгрузить(); // @skip-check query-in-loop - порционная проверка ведения учета
			ЭтоПервыйПроход = Истина;
		КонецЕсли;
		
		ОбщееКоличествоДанных = ОбщееКоличествоДанных + Результат.Количество();
		Если МодульОценкаПроизводительности <> Неопределено Тогда
			МодульОценкаПроизводительности.ЗакончитьЗамерДлительнойОперации(ОписаниеЗамера, ОбщееКоличествоДанных);
		КонецЕсли;
		
	КонецЦикла;
	
КонецПроцедуры

#КонецОбласти

#Область ПроверкаЦиклическихСсылок

Процедура НайтиЦиклическиеСсылки(ОбъектМетаданных, ПараметрыПроверки)
	
	ПолноеИмя = ОбъектМетаданных.ПолноеИмя();
	Реквизиты = ОбъектМетаданных.Реквизиты;
	
	ТекстЗапроса = 
	"ВЫБРАТЬ ПЕРВЫЕ 1000
	|	ПсевдонимЗаданнойТаблицы.Ссылка КАК ПроблемныйОбъект,
	|	ПРЕДСТАВЛЕНИЕССЫЛКИ(ПсевдонимЗаданнойТаблицы.Ссылка) КАК Представление,
	|	ПсевдонимЗаданнойТаблицы.Родитель КАК Родитель
	|ИЗ
	|	&ОбъектМетаданных КАК ПсевдонимЗаданнойТаблицы
	|ГДЕ
	|	ПсевдонимЗаданнойТаблицы.Ссылка > &Ссылка
	|
	|УПОРЯДОЧИТЬ ПО
	|	ПсевдонимЗаданнойТаблицы.Ссылка";
	
	ИмяТаблицы = СтрЗаменить(ОбъектМетаданных.ПолноеИмя(), ".", "");
	ТекстЗапроса = СтрЗаменить(ТекстЗапроса, "ПсевдонимЗаданнойТаблицы", ИмяТаблицы);
	ТекстЗапроса = СтрЗаменить(ТекстЗапроса, "&ОбъектМетаданных", ПолноеИмя);
	
	Запрос = Новый Запрос(ТекстЗапроса);
	Запрос.УстановитьПараметр("Ссылка", "");
	
	Результат = Запрос.Выполнить().Выгрузить();
	
	ПроверенныеЭлементы = Новый Соответствие;
	Пока Результат.Количество() > 0 Цикл
		
		Для Каждого СтрокаРезультата Из Результат Цикл
			
			СсылкаНаОбъект = СтрокаРезультата.ПроблемныйОбъект;
			Если ПроверенныеЭлементы[СсылкаНаОбъект] <> Неопределено Тогда
				Продолжить;
			КонецЕсли;
			
			ПроверяемыеЭлементы = Новый Массив;
			Если ПроверитьЗацикливаниеУровней(СсылкаНаОбъект, СтрокаРезультата.Родитель, ПроверяемыеЭлементы) Тогда
				
				Путь = "";
				Для Каждого ЭлементЦикла Из ПроверяемыеЭлементы Цикл
					Путь = Путь + ?(ЗначениеЗаполнено(Путь), " -> ", "") + Строка(ЭлементЦикла);
					ПроверенныеЭлементы[ЭлементЦикла] = Истина;
				КонецЦикла;
				
				ПредставлениеОбъекта = СтрокаРезультата.Представление;
				Если ЗначениеЗаполнено(Путь) Тогда
					УточнениеПроблемы = ПредставлениеОбъекта + " -> " + Путь + " -> " + ПредставлениеОбъекта;
				Иначе
					УточнениеПроблемы = ПредставлениеОбъекта + " -> " + ПредставлениеОбъекта;
				КонецЕсли;
				
				Проблема = ОписаниеПроблемы(СсылкаНаОбъект, ПараметрыПроверки); // @skip-check query-in-loop - порционная проверка ведения учета
				
				Проблема.УточнениеПроблемы = УточнениеПроблемы;
				Если Реквизиты.Найти("Ответственный") <> Неопределено Тогда
					Проблема.Вставить("Ответственный", ОбщегоНазначения.ЗначениеРеквизитаОбъекта(СсылкаНаОбъект, "Ответственный"));
				КонецЕсли;
				
				ЗаписатьПроблему(Проблема, ПараметрыПроверки); // @skip-check query-in-loop - порционная проверка ведения учета
				
			КонецЕсли;
			
		КонецЦикла;
		
		Запрос.УстановитьПараметр("Ссылка", СтрокаРезультата.ПроблемныйОбъект);
		Результат = Запрос.Выполнить().Выгрузить(); // @skip-check query-in-loop - порционная проверка ведения учета
		
	КонецЦикла;
	
КонецПроцедуры

Функция ПроверитьЗацикливаниеУровней(Знач СсылкаНаОбъект, Знач ТекущийРодитель, Знач ВсеЭлементы)
	
	Пока ЗначениеЗаполнено(ТекущийРодитель) Цикл
		Если СсылкаНаОбъект = ТекущийРодитель Тогда
			Возврат Истина;
		КонецЕсли;
		ВсеЭлементы.Добавить(ТекущийРодитель);
		ТекущийРодитель = ОбщегоНазначения.ЗначениеРеквизитаОбъекта(ТекущийРодитель, "Родитель");
	КонецЦикла;
	Возврат Ложь;
	
КонецФункции

// Параметры:
//   СтандартныеРеквизиты - ОписанияСтандартныхРеквизитов
// Возвращаемое значение:
//   Булево
//
Функция ЕстьИерархия(СтандартныеРеквизиты)
	
	ЕстьИерархия = Ложь;
	Для Каждого СтандартныйРеквизит Из СтандартныеРеквизиты Цикл
		Если СтандартныйРеквизит.Имя = "Родитель" Тогда
			ЕстьИерархия = Истина;
			Прервать;
		КонецЕсли;
	КонецЦикла;
	
	Возврат ЕстьИерархия;
	
КонецФункции

// Исправляет циклические ссылки по принципу: у кого больше терминальных элементов в подчинении, 
// тот остается родителем.
//
// Параметры:
//   Проверка - СправочникСсылка.ПравилаПроверкиУчета - проверка, найденные проблемы которой
//              исправляются данным методом.
//
Процедура ИсправитьПроблемуЦиклическихСсылок(Проверка)
	
	ТаблицаПотомства = Новый ТаблицаЗначений;
	ТаблицаПотомства.Колонки.Добавить("ЭлементЦикла");
	ТаблицаПотомства.Колонки.Добавить("КоличествоДетей", Новый ОписаниеТипов("Число"));
	
	Запрос = Новый Запрос(
	"ВЫБРАТЬ
	|	РезультатыПроверкиУчета.ПроблемныйОбъект КАК ПроблемныйОбъект,
	|	ТИПЗНАЧЕНИЯ(РезультатыПроверкиУчета.ПроблемныйОбъект) КАК ТипСсылки
	|ИЗ
	|	РегистрСведений.РезультатыПроверкиУчета КАК РезультатыПроверкиУчета
	|ГДЕ
	|	НЕ РезультатыПроверкиУчета.ИгнорироватьПроблему
	|	И РезультатыПроверкиУчета.ПравилоПроверки = &ПравилоПроверки
	|ИТОГИ ПО
	|	ТипСсылки");
	
	Запрос.УстановитьПараметр("ПравилоПроверки", Проверка);
	Результат = Запрос.Выполнить().Выгрузить(ОбходРезультатаЗапроса.ПоГруппировкам);
	
	Для Каждого СтрокаТипОбъекта Из Результат.Строки Цикл
		ПолноеИмяТаблицы = Метаданные.НайтиПоТипу(СтрокаТипОбъекта.ТипСсылки).ПолноеИмя();
		Для Каждого СтрокаОбъект Из СтрокаТипОбъекта.Строки Цикл
			НачатьТранзакцию();
			Попытка
				Блокировка = Новый БлокировкаДанных;
				ЭлементБлокировки = Блокировка.Добавить(ПолноеИмяТаблицы);
				ЭлементБлокировки.УстановитьЗначение("Ссылка", СтрокаОбъект.ПроблемныйОбъект);
				Блокировка.Заблокировать();
				
				ПроблемныйОбъектСсылка = СтрокаОбъект.ПроблемныйОбъект;
				Родитель = ОбщегоНазначения.ЗначениеРеквизитаОбъекта(ПроблемныйОбъектСсылка, "Родитель");
				
				ПроверяемыеЭлементы = Новый Массив;
				Если Не ПроверитьЗацикливаниеУровней(ПроблемныйОбъектСсылка, Родитель, ПроверяемыеЭлементы) Тогда
					ОтменитьТранзакцию();
					Продолжить;
				КонецЕсли;
					
				ПоследнийОбъектЦикла = ПроверяемыеЭлементы[ПроверяемыеЭлементы.Количество() - 1];
				
				КоличествоДетейПервого = КоличествоДочернихЭлементов(ПроблемныйОбъектСсылка, Родитель); // @skip-check query-in-loop - порционная проверка ведения учета
				КоличествоДетейВторого = КоличествоДочернихЭлементов(ПоследнийОбъектЦикла, Родитель); // @skip-check query-in-loop - порционная проверка ведения учета
				
				ПроблемныйОбъект = ?(КоличествоДетейПервого > КоличествоДетейВторого, ПроблемныйОбъектСсылка, ПоследнийОбъектЦикла);
				ПроблемныйОбъект = ПроблемныйОбъект.ПолучитьОбъект();
				ПроблемныйОбъект.Родитель = ОбщегоНазначения.МенеджерОбъектаПоПолномуИмени(ПроблемныйОбъектСсылка.Метаданные().ПолноеИмя()).ПустаяСсылка();
				ПроблемныйОбъект.ОбменДанными.Загрузка = Истина;
				ПроблемныйОбъект.Записать();
				
				ЗафиксироватьТранзакцию();
			Исключение
				ОтменитьТранзакцию();
				ВызватьИсключение;
			КонецПопытки;
		КонецЦикла;
	КонецЦикла;
	
КонецПроцедуры

Функция КоличествоДочернихЭлементов(СсылкаНаОбъект, ИсключениеВыборки, Знач НачальноеЗначение = 0)
	
	КоличествоДетей = НачальноеЗначение;
	
	Выгрузка = ДочерниеЭлементыРодителя(СсылкаНаОбъект, ИсключениеВыборки);
	КоличествоДетей = КоличествоДетей + Выгрузка.Количество();
	Для Каждого ЭлементПотомства Из Выгрузка Цикл
		КоличествоДетей = КоличествоДочернихЭлементов(ЭлементПотомства.Ссылка, ИсключениеВыборки, КоличествоДетей); // @skip-check query-in-loop - порционная проверка ведения учета
	КонецЦикла;
	Возврат КоличествоДетей;
	
КонецФункции

// Возвращаемое значение:
//   ТаблицаЗначений:
//   * Ссылка - ЛюбаяСсылка
//
Функция ДочерниеЭлементыРодителя(СсылкаНаОбъект, Знач ИсключениеВыборки)
	Перем Результат;
	ТекстЗапроса =
	"ВЫБРАТЬ
	|	ПсевдонимЗаданнойТаблицы.Ссылка КАК Ссылка
	|ИЗ
	|	&ОбъектМетаданных КАК ПсевдонимЗаданнойТаблицы
	|ГДЕ
	|	ПсевдонимЗаданнойТаблицы.Родитель = &Родитель
	|	И ПсевдонимЗаданнойТаблицы.Ссылка <> &ИсключениеВыборки";
	
	ПолноеИмя = СсылкаНаОбъект.Метаданные().ПолноеИмя();
	ИмяТаблицы = СтрЗаменить(ПолноеИмя, ".", "");
	ТекстЗапроса = СтрЗаменить(ТекстЗапроса, "ПсевдонимЗаданнойТаблицы", ИмяТаблицы);
	ТекстЗапроса = СтрЗаменить(ТекстЗапроса, "&ОбъектМетаданных", ПолноеИмя);
	
	Запрос = Новый Запрос(ТекстЗапроса);
	Запрос.УстановитьПараметр("Родитель",          СсылкаНаОбъект);
	Запрос.УстановитьПараметр("ИсключениеВыборки", ИсключениеВыборки);
	Результат = Запрос.Выполнить();
	
	Выгрузка        = Результат.Выгрузить();
	Возврат Выгрузка
КонецФункции

#КонецОбласти

#Область ПроверкаОтсутствующихПредопределенныхЭлементов

Процедура НайтиОтсутствующиеПредопределенныеЭлементы(ОбъектМетаданных, ПараметрыПроверки)
	
	ПолноеИмя                = ОбъектМетаданных.ПолноеИмя();
	ПредопределенныеЭлементы = ОбъектМетаданных.ПолучитьИменаПредопределенных();
	Для Каждого ПредопределенныйЭлемент Из ПредопределенныеЭлементы Цикл
		
		Если СтрНачинаетсяС(ВРег(ПредопределенныйЭлемент), "УДАЛИТЬ") Тогда
			Продолжить;
		КонецЕсли;
		
		НайденныйЭлемент = ОбщегоНазначения.ПредопределенныйЭлемент(ПолноеИмя + "." + ПредопределенныйЭлемент);
		Если НайденныйЭлемент <> Неопределено Тогда
			Продолжить;
		КонецЕсли;
			
		Проблема = ОписаниеПроблемы(ОбщегоНазначения.ИдентификаторОбъектаМетаданных(ОбъектМетаданных), ПараметрыПроверки); // @skip-check query-in-loop - порционная проверка ведения учета
		Проблема.УточнениеПроблемы = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(НСтр("ru = 'Предопределенный элемент ""%1"" отсутствует в информационной базе.'"), ПредопределенныйЭлемент);
		ЗаписатьПроблему(Проблема, ПараметрыПроверки); // @skip-check query-in-loop - порционная проверка ведения учета
		
	КонецЦикла;
	
КонецПроцедуры

#КонецОбласти

#Область ПроверкаДублированияПредопределенныхЭлементов

Процедура НайтиДублиПредопределенныхЭлементов(ВидМетаданных, ПараметрыПроверки)
	
	ШаблонВременнойТаблицы =
	"ВЫБРАТЬ
	|	""&Таблица"" КАК ПолноеИмя,
	|	Таблица.ИмяПредопределенныхДанных КАК ИмяПредопределенныхДанных 
	|ПОМЕСТИТЬ
	|#ВременнаяТаблица
	|ИЗ
	|	&Таблица КАК Таблица
	|ГДЕ Таблица.Предопределенный
	|
	|СГРУППИРОВАТЬ ПО
	|	Таблица.ИмяПредопределенныхДанных
	|
	|ИМЕЮЩИЕ
	|	КОЛИЧЕСТВО(РАЗЛИЧНЫЕ Таблица.Ссылка) > 1";
	
	ШаблонВыборки =
	"ВЫБРАТЬ
	|	&ПроблемныйОбъект КАК ПроблемныйОбъект, 
	|	ВременнаяТаблица.ИмяПредопределенныхДанных КАК ИмяПредопределенныхДанных,
	|	ПредставлениеСсылки(Таблица.Ссылка) КАК СсылкаНаДублирующийсяЭлемент
	|ИЗ
	|	&Таблица КАК Таблица
	|		ВНУТРЕННЕЕ СОЕДИНЕНИЕ #ВременнаяТаблица КАК ВременнаяТаблица
	|		ПО Таблица.ИмяПредопределенныхДанных = ВременнаяТаблица.ИмяПредопределенныхДанных";
	
	ИтогиВыборки = "
	|ИТОГИ ПО ПроблемныйОбъект, ИмяПредопределенныхДанных"; // @query-part-1
	
	ТекстВременнойТаблицы = "";
	ТекстВыборки = "";
	
	Запрос = Новый Запрос;
	Для Каждого ОбъектМетаданных Из ВидМетаданных Цикл
		
		ИмяТаблицы = ОбъектМетаданных.ПолноеИмя();
		Если ЭтоНеразделенныйОбъектМетаданных(ИмяТаблицы) Тогда
			Продолжить;
		КонецЕсли;
		
		Если СтрНачинаетсяС(ОбъектМетаданных.Имя, "Удалить") Тогда
			Продолжить;
		КонецЕсли;
		
		Если ОбъектМетаданных.ОбновлениеПредопределенныхДанных = Метаданные.СвойстваОбъектов.ОбновлениеПредопределенныхДанных.НеОбновлятьАвтоматически Тогда
			Продолжить;
		КонецЕсли;
		
		ИмяВременнойТаблицы = "ВТ_" + СтрЗаменить(ИмяТаблицы, ".", "");
		
		ТекстЗапроса = СтрЗаменить(ШаблонВременнойТаблицы, "#ВременнаяТаблица", ИмяВременнойТаблицы);
		ТекстЗапроса = СтрЗаменить(ТекстЗапроса, "&Таблица", ИмяТаблицы);
		ТекстВременнойТаблицы = ТекстВременнойТаблицы + ?(ЗначениеЗаполнено(ТекстВременнойТаблицы), ";", "") + ТекстЗапроса;
		
		СуффиксПараметра = СтрЗаменить(ИмяТаблицы, ".", "_");
		ИмяПараметра = "&ПроблемныйОбъект" + СуффиксПараметра;
		ТекстЗапроса = СтрЗаменить(ШаблонВыборки, "&ПроблемныйОбъект",	ИмяПараметра);
		ТекстЗапроса = СтрЗаменить(ТекстЗапроса, "&Таблица", ИмяТаблицы);
		ТекстЗапроса = СтрЗаменить(ТекстЗапроса, "#ВременнаяТаблица", ИмяВременнойТаблицы);
		ТекстВыборки = ТекстВыборки + ?(ЗначениеЗаполнено(ТекстВыборки), " ОБЪЕДИНИТЬ ВСЕ ", "") + ТекстЗапроса;
		Запрос.УстановитьПараметр("ПроблемныйОбъект" + СуффиксПараметра, ОбщегоНазначения.ИдентификаторОбъектаМетаданных(ИмяТаблицы));
		
	КонецЦикла;
	
	Если Не ЗначениеЗаполнено(ТекстВременнойТаблицы) Тогда
		Возврат;
	КонецЕсли;
   
	Запрос.Текст = ТекстВременнойТаблицы + ";" + ТекстВыборки + ИтогиВыборки;
	Результат = Запрос.Выполнить().Выбрать(ОбходРезультатаЗапроса.ПоГруппировкам);
	Пока Результат.Следующий() Цикл
		
		УточнениеПроблемы = "";
		РезультатПоИмениПредопределенного = Результат.Выбрать(ОбходРезультатаЗапроса.ПоГруппировкам);
		
		Пока РезультатПоИмениПредопределенного.Следующий() Цикл
			
			ИмяПредопределенныхДанных = РезультатПоИмениПредопределенного.ИмяПредопределенныхДанных;
			Если СтрНачинаетсяС(ВРег(ИмяПредопределенныхДанных), "УДАЛИТЬ") Тогда
				Продолжить;
			КонецЕсли;
			
			УточнениеПроблемы = УточнениеПроблемы + ?(ЗначениеЗаполнено(УточнениеПроблемы), Символы.ПС, "")
				+ НСтр("ru = 'Имя предопределенного элемента:'") + " """ + ИмяПредопределенныхДанных + """"
				+ Символы.ПС + НСтр("ru = 'Ссылки на предопределенный элемент:'");
				
			ДетальныеЗаписи = РезультатПоИмениПредопределенного.Выбрать();
			Пока ДетальныеЗаписи.Следующий() Цикл
				УточнениеПроблемы = УточнениеПроблемы + ?(ЗначениеЗаполнено(УточнениеПроблемы), Символы.ПС, "")
					+ " • """ + ДетальныеЗаписи.СсылкаНаДублирующийсяЭлемент + """";
			КонецЦикла;
			
		КонецЦикла;
		
		Если Не ЗначениеЗаполнено(УточнениеПроблемы) Тогда
			Продолжить;
		КонецЕсли;
		
		Проблема = ОписаниеПроблемы(Результат.ПроблемныйОбъект, ПараметрыПроверки); // @skip-check query-in-loop - порционная проверка ведения учета
		Проблема.УточнениеПроблемы = УточнениеПроблемы;
		ЗаписатьПроблему(Проблема, ПараметрыПроверки); // @skip-check query-in-loop - порционная проверка ведения учета
		
	КонецЦикла;
	
КонецПроцедуры

#КонецОбласти

#Область Прочее

Функция ЕстьОбязательныеРеквизиты(ОбъектМетаданных)
	
	Для Каждого Измерение Из ОбъектМетаданных.Измерения Цикл
		Если Измерение.ПроверкаЗаполнения = ПроверкаЗаполнения.ВыдаватьОшибку Тогда
			Возврат Истина;
		КонецЕсли;
	КонецЦикла;
	
	Для Каждого Реквизит Из ОбъектМетаданных.Реквизиты Цикл
		Если Реквизит.ПроверкаЗаполнения = ПроверкаЗаполнения.ВыдаватьОшибку Тогда
			Возврат Истина;
		КонецЕсли;
	КонецЦикла;
	
	Для Каждого Ресурс Из ОбъектМетаданных.Ресурсы Цикл
		Если Ресурс.ПроверкаЗаполнения = ПроверкаЗаполнения.ВыдаватьОшибку Тогда
			Возврат Истина;
		КонецЕсли;
	КонецЦикла;
	
	Для Каждого СтандартныйРеквизит Из ОбъектМетаданных.СтандартныеРеквизиты Цикл
		Если СтандартныйРеквизит.ПроверкаЗаполнения = ПроверкаЗаполнения.ВыдаватьОшибку Тогда
			Возврат Истина;
		КонецЕсли;
	КонецЦикла;
	
	Возврат Ложь;
	
КонецФункции

Функция ПрочитанныеПараметры(ПараметрыВыполненияПроверки)
	Результат = Новый Структура("ПолноеИмя, ОбъектМетаданных, ОбластьПроверки");
	Если ПараметрыВыполненияПроверки.Количество() <> 1 Тогда
		Возврат Неопределено;
	КонецЕсли;
	
	СтруктураПараметров = ПараметрыВыполненияПроверки[0];
	Если ТипЗнч(СтруктураПараметров) <> Тип("Структура") Тогда
		Возврат Неопределено;
	КонецЕсли;
	
	Если СтруктураПараметров.Свойство1 <> "ПроверяемыйОбъект" Тогда
		Возврат Неопределено;
	КонецЕсли;
	
	Результат.ПолноеИмя = СтруктураПараметров.Свойство2;
	Если СтандартныеПодсистемыСервер.ЭтоТаблицаРегистра(Результат.ПолноеИмя) Тогда
		Результат.ОбластьПроверки = "Регистры";
	Иначе
		Результат.ОбластьПроверки = "СсылочныеОбъекты";
	КонецЕсли;
	Результат.ОбъектМетаданных = ОбщегоНазначения.ОбъектМетаданныхПоПолномуИмени(Результат.ПолноеИмя);
	
	Возврат Результат;
КонецФункции

Функция ПредставлениеПроблемногоОбъекта(ПроблемныйОбъект, ПроблемныйОбъектПредставление, ДополнительнаяИнформация) Экспорт
	
	Результат = ПроблемныйОбъектПредставление + " (" + ПроблемныйОбъект.Метаданные().Представление() + ")";
	Если ТипЗнч(ПроблемныйОбъект) <> Тип("СправочникСсылка.ИдентификаторыОбъектовМетаданных") Тогда
		Возврат Результат;
	КонецЕсли;
	
	Результат = Строка(ПроблемныйОбъект) + "<РасшифровкаСписка>" + Символы.ПС + ПроблемныйОбъект.ПолноеИмя;
	Если Не ОбщегоНазначения.ЭтоРегистр(ОбщегоНазначения.ОбъектМетаданныхПоПолномуИмени(ПроблемныйОбъект.ПолноеИмя)) Тогда
		Возврат Результат;
	КонецЕсли;
		
	СтруктураНабора  = ДополнительнаяИнформация.Получить();
	Если ТипЗнч(СтруктураНабора) <> Тип("Структура") Тогда
		Возврат Результат;
	КонецЕсли;
	
	Для Каждого ЭлементНабора Из СтруктураНабора Цикл
		
		ЗначениеОтбора    = ЭлементНабора.Значение;
		ТипЗначенияОтбора = ТипЗнч(ЗначениеОтбора);
		ИнформацияОТипе   = "";
		
		Если ТипЗначенияОтбора = Тип("Число") Тогда
			ИнформацияОТипе = "Число";
		ИначеЕсли ТипЗначенияОтбора = Тип("Строка") Тогда
			ИнформацияОТипе = "Строка";
		ИначеЕсли ТипЗначенияОтбора = Тип("Булево") Тогда
			ИнформацияОТипе = "Булево";
		ИначеЕсли ТипЗначенияОтбора = Тип("Дата") Тогда
			ИнформацияОТипе = "Дата";
		ИначеЕсли ОбщегоНазначения.ЭтоСсылка(ТипЗначенияОтбора) Тогда
			ИнформацияОТипе = ЗначениеОтбора.Метаданные().ПолноеИмя();
		КонецЕсли;
		
		Результат = Результат + Символы.ПС + Строка(ЭлементНабора.Ключ) + "~~~" + ИнформацияОТипе + "~~~" + Строка(XMLСтрока(ЗначениеОтбора));
		
	КонецЦикла;
	
	Возврат Результат;
	
КонецФункции

Функция РасшифровкаЯчейки(Ссылка, ПравилоПроверки, ВидПроверки, УточнениеПроблемы) Экспорт
	Если Не ОбщегоНазначения.ЭтоСсылка(ТипЗнч(Ссылка)) Тогда
		Возврат "";
	КонецЕсли;
	
	Расшифровка = Ссылка.Метаданные().ПолноеИмя() + ";"
		+ Ссылка.УникальныйИдентификатор() + ";"
		+ ПравилоПроверки.УникальныйИдентификатор() + ";"
		+ ВидПроверки.УникальныйИдентификатор() + ";"
		+ УточнениеПроблемы;
	
	Возврат Расшифровка;
КонецФункции

// См. КонтрольВеденияУчета.ПодсистемаДоступна.
Функция ПодсистемаДоступна() Экспорт
	
	Возврат ПравоДоступа("Просмотр", Метаданные.РегистрыСведений.РезультатыПроверкиУчета);
	
КонецФункции

Функция ЭтоНеразделенныйОбъектМетаданных(ПолноеИмя)
	
	Если Не ОбщегоНазначения.РазделениеВключено() Тогда
		Возврат Ложь;
	КонецЕсли;
	
	Если ОбщегоНазначения.ПодсистемаСуществует("ТехнологияСервиса.БазоваяФункциональность") Тогда
		
		МодульРаботаВМоделиСервиса = ОбщегоНазначения.ОбщийМодуль("РаботаВМоделиСервиса");
		Возврат Не МодульРаботаВМоделиСервиса.ЭтоРазделенныйОбъектМетаданных(ПолноеИмя);
		
	КонецЕсли;
	
	Возврат Ложь;
	
КонецФункции

Процедура ЗаполнитьИндексКартинки(Строки, СтрокаСписка, КлючСтроки, КолонкаИндикатора, ПроблемныеОбъекты) Экспорт
	
	Если Не СтрокаСписка.Данные.Свойство(КолонкаИндикатора) Тогда
		Возврат;
	КонецЕсли;
	
	ТекстЯчейки = СтрокаСписка.Оформление.Получить(КолонкаИндикатора).НайтиЗначениеПараметра(Новый ПараметрКомпоновкиДанных("Текст"));
	
	НайденнаяСтрока = ПроблемныеОбъекты.Найти(КлючСтроки, "ПроблемныйОбъект");
	Если НайденнаяСтрока = Неопределено Тогда
		СтрокаСписка.Данные[КолонкаИндикатора] = 0;
		Если ТекстЯчейки <> Неопределено Тогда
			ТекстЯчейки.Значение = 0;
		КонецЕсли;
	Иначе
		Если НайденнаяСтрока.ПорядокПроблемы = 0
			Или НайденнаяСтрока.ПорядокПроблемы = 1 Тогда
			СтрокаСписка.Данные[КолонкаИндикатора] = 1
		ИначеЕсли НайденнаяСтрока.ПорядокПроблемы = 2
			Или НайденнаяСтрока.ПорядокПроблемы = 3 Тогда
			СтрокаСписка.Данные[КолонкаИндикатора] = 2
		ИначеЕсли НайденнаяСтрока.ПорядокПроблемы = 4 Тогда
			СтрокаСписка.Данные[КолонкаИндикатора] = 3
		Иначе
			СтрокаСписка.Данные[КолонкаИндикатора] = 1
		КонецЕсли;
		
		Если ТекстЯчейки <> Неопределено Тогда
			ТекстЯчейки.Значение = 1;
		КонецЕсли;
	КонецЕсли;
	
КонецПроцедуры

Функция ОшибкиЗаполненияОбъекта()
	
	УточнениеПроблемы = "";
	Для Каждого ПользовательскоеСообщение Из ПолучитьСообщенияПользователю(Истина) Цикл
		УточнениеПроблемы = УточнениеПроблемы + ?(ЗначениеЗаполнено(УточнениеПроблемы), Символы.ПС, "") + ПользовательскоеСообщение.Текст;
	КонецЦикла;
	
	Возврат ?(ПустаяСтрока(УточнениеПроблемы), НСтр("ru = 'Для подробной информации откройте форму объекта.'"), УточнениеПроблемы);
	
КонецФункции

Функция ПроверкаУжеВыполняется(ИдентификаторРегламентногоЗадания)
	
	Результат = Ложь;
	Если ЗначениеЗаполнено(ИдентификаторРегламентногоЗадания) Тогда
		Задание = ФоновыеЗадания.НайтиПоУникальномуИдентификатору(Новый УникальныйИдентификатор(ИдентификаторРегламентногоЗадания));
		Если Задание <> Неопределено И Задание.Состояние = СостояниеФоновогоЗадания.Активно Тогда
			Результат = Истина;
		КонецЕсли;
	КонецЕсли;
	
	Возврат Результат;
	
КонецФункции

// См. КонтрольВеденияУчета.ОчиститьРезультатыПредыдущихПроверок.
Процедура ОчиститьРезультатыПредыдущихПроверок(Проверка, ПараметрыВыполненияПроверки) Экспорт
	
	Для Каждого ПараметрВыполненияПроверки Из ПараметрыВыполненияПроверки Цикл
		
		ВидПроверки = ВидПроверки(ПараметрВыполненияПроверки, Истина); // @skip-check query-in-loop - порционная проверка ведения учета
		Если Не ЗначениеЗаполнено(ВидПроверки) Тогда
			Продолжить;
		КонецЕсли;
		
		Набор = РегистрыСведений.РезультатыПроверкиУчета.СоздатьНаборЗаписей();
		Набор.Отбор.ПравилоПроверки.Установить(Проверка);
		Набор.Отбор.ВидПроверки.Установить(ВидПроверки);
		Набор.Отбор.ИгнорироватьПроблему.Установить(Ложь);
		Набор.Записать();
		
	КонецЦикла;
	
КонецПроцедуры

Процедура ОчиститьРезультатыПередВыполнениемПроверки(Проверка)
	Набор = РегистрыСведений.РезультатыПроверкиУчета.СоздатьНаборЗаписей();
	Набор.Отбор.ПравилоПроверки.Установить(Проверка);
	Набор.Отбор.ИгнорироватьПроблему.Установить(Ложь);
	Набор.Записать();
КонецПроцедуры

Функция РезультатПоследнейПроверкиОбъекта(ПараметрыВыполненияПроверки)
	Запрос = Новый Запрос;
	Запрос.УстановитьПараметр("ПравилоПроверки", ПараметрыВыполненияПроверки.Проверка);
	Запрос.УстановитьПараметр("ПроблемныеОбъекты", ПараметрыВыполненияПроверки.ПроверяемыеОбъекты);
	Запрос.Текст =
		"ВЫБРАТЬ
		|	РезультатыПроверкиУчета.ПроблемныйОбъект КАК ПроблемныйОбъект,
		|	РезультатыПроверкиУчета.ПравилоПроверки КАК ПравилоПроверки,
		|	РезультатыПроверкиУчета.ВидПроверки КАК ВидПроверки,
		|	РезультатыПроверкиУчета.КлючУникальности КАК КлючУникальности,
		|	РезультатыПроверкиУчета.ИгнорироватьПроблему КАК ИгнорироватьПроблему
		|ИЗ
		|	РегистрСведений.РезультатыПроверкиУчета КАК РезультатыПроверкиУчета
		|ГДЕ
		|	РезультатыПроверкиУчета.ПравилоПроверки = &ПравилоПроверки
		|	И РезультатыПроверкиУчета.ПроблемныйОбъект В (&ПроблемныеОбъекты)
		|	И РезультатыПроверкиУчета.ИгнорироватьПроблему = ЛОЖЬ";
	
	Возврат Запрос.Выполнить().Выгрузить();
КонецФункции

Процедура УдалитьРезультатыПредыдущейПроверки(РезультатПоследнейПроверки)
	
	Для Каждого Строка Из РезультатПоследнейПроверки Цикл
		Набор = РегистрыСведений.РезультатыПроверкиУчета.СоздатьНаборЗаписей();
		Набор.Отбор.ПроблемныйОбъект.Установить(Строка.ПроблемныйОбъект);
		Набор.Отбор.ПравилоПроверки.Установить(Строка.ПравилоПроверки);
		Набор.Отбор.ВидПроверки.Установить(Строка.ВидПроверки);
		Набор.Отбор.КлючУникальности.Установить(Строка.КлючУникальности);
		Набор.Отбор.ИгнорироватьПроблему.Установить(Строка.ИгнорироватьПроблему);
		
		УстановитьПривилегированныйРежим(Истина);
		Набор.Записать();
		УстановитьПривилегированныйРежим(Ложь);
	КонецЦикла;
	
КонецПроцедуры

// Выполняет проверку превышения количества итераций проверки допустимого лимита.
//
// Параметры:
//   ПараметрыПроверки - см. параметр ПараметрыПроверки в КонтрольВеденияУчета.ОписаниеПроблемы.
//
// Возвращаемое значение:
//   Булево - признак того, достигнута ли последняя итерация.
//
Функция ЭтоПоследняяИтерацияПроверки(ПараметрыПроверки)
	
	ЭтоПоследняяИтерация = Ложь;
	
	Если ПараметрыПроверки.ЛимитПроблем <> 0 Тогда
		Если ПараметрыПроверки.ИтерацияПроверки > ПараметрыПроверки.ЛимитПроблем Тогда
			ЭтоПоследняяИтерация = Истина;
		Иначе
			ПараметрыПроверки.Вставить("ИтерацияПроверки", ПараметрыПроверки.ИтерацияПроверки + 1);
		КонецЕсли;
	КонецЕсли;
	
	Возврат ЭтоПоследняяИтерация;
	
КонецФункции

// Возвращает строковое представление вида объектов метаданных по типу объекта.
// Ограничение: не обрабатываются точки маршрутов бизнес-процессов.
//
// Параметры:
//  СсылкаНаОбъект - ЛюбаяСсылка - ссылка на проблемный объект.
//
// Возвращаемое значение:
//  Строка - представление вида объектов метаданных. Например: "Справочник", "Документ".
//
Функция ПредставлениеОбъектаПоТипу(СсылкаНаОбъект)
	
	ТипОбъекта = ТипЗнч(СсылкаНаОбъект);
	
	Если Справочники.ТипВсеСсылки().СодержитТип(ТипОбъекта) Тогда
		
		Возврат НСтр("ru = 'элементе справочника'");
	
	ИначеЕсли Документы.ТипВсеСсылки().СодержитТип(ТипОбъекта) Тогда
		Возврат НСтр("ru = 'документе'");
	
	ИначеЕсли БизнесПроцессы.ТипВсеСсылки().СодержитТип(ТипОбъекта) Тогда
		Возврат НСтр("ru = 'бизнес процессе'");
	
	ИначеЕсли ПланыВидовХарактеристик.ТипВсеСсылки().СодержитТип(ТипОбъекта) Тогда
		Возврат НСтр("ru = 'плане видов характеристик'");
	
	ИначеЕсли ПланыСчетов.ТипВсеСсылки().СодержитТип(ТипОбъекта) Тогда
		Возврат НСтр("ru = 'плане счетов'");
	
	ИначеЕсли ПланыВидовРасчета.ТипВсеСсылки().СодержитТип(ТипОбъекта) Тогда
		Возврат НСтр("ru = 'плане видов расчета'");
	
	ИначеЕсли Задачи.ТипВсеСсылки().СодержитТип(ТипОбъекта) Тогда
		Возврат НСтр("ru = 'задаче'");
	
	ИначеЕсли ПланыОбмена.ТипВсеСсылки().СодержитТип(ТипОбъекта) Тогда
		Возврат НСтр("ru = 'плане обмена'");
	
	ИначеЕсли Перечисления.ТипВсеСсылки().СодержитТип(ТипОбъекта) Тогда
		Возврат НСтр("ru = 'перечислении'");
	
	Иначе
		ВызватьИсключение СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(НСтр("ru = 'Неверный тип значения параметра (%1)'"), Строка(ТипОбъекта));
	
	КонецЕсли;
	
КонецФункции

// Возвращает типы Строка, Массив и СправочникСсылка.ВидыПроверок
// для проверки параметров методов, работающих с видами проверок.
//
// Возвращаемое значение:
//    Массив - объектные типы.
//
Функция ОписаниеТипаВидПроверки() Экспорт
	
	МассивТипов = Новый Массив;
	МассивТипов.Добавить(Тип("Строка"));
	МассивТипов.Добавить(Тип("Массив"));
	МассивТипов.Добавить(Тип("СправочникСсылка.ВидыПроверок"));
	
	Возврат МассивТипов;
	
КонецФункции

// Возвращает допустимые типы параметров проверки.
//
// Возвращаемое значение:
//   Структура:
//      * ВажностьПроблемы - ПеречислениеСсылка.ВажностьПроблемыУчета
//      * ГлобальныеНастройки - Структура
//      * ДатаНачалаПроверки - Дата
//      * Идентификатор - Строка
//      * ИдентификаторРегламентногоЗадания - Строка
//      * ИтерацияПроверки - Число
//      * ЛимитПроблем - Число
//      * Наименование - Строка
//      * Проверка - СправочникСсылка.ПравилаПроверкиУчета
//
Функция ОжидаемыеТипыСвойствПараметровПроверки() Экспорт
	
	ОжидаемыеТипыСвойств = Новый Структура;
	ОжидаемыеТипыСвойств.Вставить("ВажностьПроблемы",                  Тип("ПеречислениеСсылка.ВажностьПроблемыУчета"));
	ОжидаемыеТипыСвойств.Вставить("ГлобальныеНастройки",               Тип("Структура"));
	ОжидаемыеТипыСвойств.Вставить("ДатаНачалаПроверки",                Тип("Дата"));
	ОжидаемыеТипыСвойств.Вставить("Идентификатор",                     Тип("Строка"));
	ОжидаемыеТипыСвойств.Вставить("ИдентификаторРегламентногоЗадания", Тип("Строка"));
	ОжидаемыеТипыСвойств.Вставить("ИтерацияПроверки",                  Тип("Число"));
	ОжидаемыеТипыСвойств.Вставить("ЛимитПроблем",                      Тип("Число"));
	ОжидаемыеТипыСвойств.Вставить("Наименование",                      Тип("Строка"));
	ОжидаемыеТипыСвойств.Вставить("Проверка",                          Тип("СправочникСсылка.ПравилаПроверкиУчета"));
	
	Возврат ОжидаемыеТипыСвойств;
	
КонецФункции

// Возвращает допустимые типы свойств вида проверки - см. реквизиты Свойство1, Свойство2,... справочника ВидыПроверок. 
//
// Возвращаемое значение:
//   ОписаниеТипов - все ссылочные типы, а также типы Булево, Строка, Число и Дата.
//
Функция ОжидаемыеТипыСвойствВидовПроверок() Экспорт
	
	МассивТипов = Новый Массив;
	МассивТипов.Добавить(Тип("Булево"));
	МассивТипов.Добавить(Тип("Строка"));
	МассивТипов.Добавить(Тип("Дата"));
	МассивТипов.Добавить(Тип("Число"));
	
	Возврат Новый ОписаниеТипов(ОбщегоНазначения.ОписаниеТипаВсеСсылки(), МассивТипов);
	
КонецФункции

// Возвращает допустимые типы свойств описания проблемы.
//
// Параметры:
//   ПолноеОписаниеПроблемы          - Булево - влияет на состав свойств возвращаемого значения.
//
// Возвращаемое значение:
//   Структура:
//        * ПроблемныйОбъект         - ЛюбаяСсылка - ссылка на объект "Источник" проблем.
//        * ПравилоПроверки          - СправочникСсылка.ПравилаПроверкиУчета - ссылка на выполненную проверку.
//        * ВидПроверки              - СправочникСсылка.ВидыПроверок - ссылка на вид проверки, к которому 
//                                     относится выполненная проверка.
//        * УточнениеПроблемы        - Строка - строка-уточнение найденной проблемы.
//        * КлючУникальности         - УникальныйИдентификатор - ключ уникальности проблемы. 
//                                     Возвращается, если параметр ПолноеОписаниеПроблемы = Истина.
//        * ВажностьПроблемы         - ПеречислениеСсылка.ВажностьПроблемыУчета - важность проблемы учета
//                                     "Информация", "Предупреждение", "Ошибка" и "ПолезныйСовет".
//                                     Возвращается, если параметр ПолноеОписаниеПроблемы = Истина.
//        * Ответственный            - СправочникСсылка.Пользователи - заполнен если есть возможность
//                                     идентифицировать ответственного в проблемном объекте.
//                                     Возвращается, если параметр ПолноеОписаниеПроблемы = Истина.
//        * ИгнорироватьПроблему     - Булево - флаг игнорирования проблемы. Если имеет значение "Истина",
//                                     запись о проблеме игнорируется подсистемой.
//                                     Возвращается, если параметр ПолноеОписаниеПроблемы = Истина.
//        * ДополнительнаяИнформация - ХранилищеЗначения - служебное свойство с дополнительными
//                                     сведениями, связанными с выявленной проблемой.
//                                     Возвращается, если параметр ПолноеОписаниеПроблемы = Истина.
//        * Выявлено                 - Дата - серверное время идентификации проблемы.
//                                     Возвращается, если параметр ПолноеОписаниеПроблемы = Истина.
//
Функция ОжидаемыеТипыСвойствОписанияПроблемы(Знач ПолноеОписаниеПроблемы = Истина) Экспорт
	
	ОжидаемыеТипыСвойств = Новый Структура;
	ОжидаемыеТипыСвойств.Вставить("ПроблемныйОбъект",  ОбщегоНазначения.ОписаниеТипаВсеСсылки());
	ОжидаемыеТипыСвойств.Вставить("ПравилоПроверки",   Тип("СправочникСсылка.ПравилаПроверкиУчета"));
	ОжидаемыеТипыСвойств.Вставить("ВидПроверки",       Тип("СправочникСсылка.ВидыПроверок"));
	ОжидаемыеТипыСвойств.Вставить("УточнениеПроблемы", Тип("Строка"));
	
	Если ПолноеОписаниеПроблемы Тогда
		ОжидаемыеТипыСвойств.Вставить("ВажностьПроблемы",         Тип("ПеречислениеСсылка.ВажностьПроблемыУчета"));
		ОжидаемыеТипыСвойств.Вставить("Выявлено",                 Тип("Дата"));
		ОжидаемыеТипыСвойств.Вставить("КлючУникальности",         Тип("УникальныйИдентификатор"));
		ОжидаемыеТипыСвойств.Вставить("ДополнительнаяИнформация", Тип("ХранилищеЗначения"));
		ТипыОтветственного = Новый Массив;
		ТипыОтветственного.Добавить(Тип("СправочникСсылка.Пользователи"));
		ТипыОтветственного.Добавить(Тип("Неопределено"));
		ОжидаемыеТипыСвойств.Вставить("Ответственный", ТипыОтветственного);
	КонецЕсли;
	
	Возврат ОжидаемыеТипыСвойств;
	
КонецФункции

// Возвращаемое значение:
//   Массив из ОбъектМетаданныхСправочник
//
Функция СсылочныеВидыОбъектовМетаданных()
	
	Результат = Новый Массив;
	Результат.Добавить(Метаданные.Справочники);
	Результат.Добавить(Метаданные.Документы);
	Результат.Добавить(Метаданные.ПланыОбмена);
	Результат.Добавить(Метаданные.ПланыВидовХарактеристик);
	Результат.Добавить(Метаданные.ПланыСчетов);
	Результат.Добавить(Метаданные.ПланыВидовРасчета);
	Результат.Добавить(Метаданные.БизнесПроцессы);
	Результат.Добавить(Метаданные.Задачи);
	Возврат Результат;
	
КонецФункции

Функция РегистрыКакОбъектыМетаданных()
	
	Результат = Новый Массив;
	Результат.Добавить(Метаданные.РегистрыБухгалтерии);
	Результат.Добавить(Метаданные.РегистрыНакопления);
	Результат.Добавить(Метаданные.РегистрыРасчета);
	Результат.Добавить(Метаданные.РегистрыСведений);
	Возврат Результат;
	
КонецФункции

Функция КартинкаПроблемы(Настройки)
	КартинкаИндикатораПроблем = Настройки.КартинкаИндикатораПроблем;
	ВажностьПроблемы = Настройки.ВажностьПроблемы;
	
	Если КартинкаИндикатораПроблем <> Неопределено И КартинкаИндикатораПроблем <> БиблиотекаКартинок.Предупреждение Тогда
		Картинка = КартинкаИндикатораПроблем;
	ИначеЕсли ВажностьПроблемы = Перечисления.ВажностьПроблемыУчета.Ошибка
		Или ВажностьПроблемы = Перечисления.ВажностьПроблемыУчета.Предупреждение Тогда
		Картинка = БиблиотекаКартинок.Предупреждение;
	ИначеЕсли ВажностьПроблемы = Перечисления.ВажностьПроблемыУчета.ВажнаяИнформация
		Или ВажностьПроблемы = Перечисления.ВажностьПроблемыУчета.Информация Тогда
		Картинка = БиблиотекаКартинок.Информация;
	ИначеЕсли ВажностьПроблемы = Перечисления.ВажностьПроблемыУчета.ПолезныйСовет Тогда
		Картинка = БиблиотекаКартинок.ПолезныйСоветКонтрольВеденияУчета;
	Иначе
		Картинка = БиблиотекаКартинок.Предупреждение;
	КонецЕсли;
	
	Возврат Картинка;
КонецФункции

#Область ИгнорированиеПроблемСлужебная

// Формирует контрольную сумму измерений ПроблемныйОбъект, ПравилоПроверки, ВидПроверки 
// и ресурса УточнениеПроблемы по алгоритму MD5.
//
// Параметры:
//   Проблема - см. одноименный параметр КонтрольВеденияУчета.ЗаписатьПроблему.
//
// Возвращаемое значение:
//    Строка - контрольная сумма измерений регистра результатов проверок: ПроблемныйОбъект,
//             ПравилоПроверки, ВидПроверки и ресурса УточнениеПроблемы по алгоритму MD5.
//
Функция КонтрольнаяСуммаПроблемы(Проблема) Экспорт
	
	СтруктураПроблемы = Новый Структура("ПроблемныйОбъект, ПравилоПроверки, ВидПроверки, УточнениеПроблемы");
	ЗаполнитьЗначенияСвойств(СтруктураПроблемы, Проблема);
	Возврат ОбщегоНазначения.КонтрольнаяСуммаСтрокой(СтруктураПроблемы);
	
КонецФункции

// Возвращает Истина, если проблема была проигнорирована.
//
// Параметры:
//   КонтрольнаяСумма - Строка - контрольная сумма записи регистра по алгоритму MD5.
//
Функция ЭтаПроблемаИгнорируется(КонтрольнаяСумма)
	
	Запрос = Новый Запрос(
	"ВЫБРАТЬ ПЕРВЫЕ 1
	|	РезультатыПроверкиУчета.ПроблемныйОбъект КАК ПроблемныйОбъект
	|ИЗ
	|	РегистрСведений.РезультатыПроверкиУчета КАК РезультатыПроверкиУчета
	|ГДЕ
	|	РезультатыПроверкиУчета.КонтрольнаяСумма = &КонтрольнаяСумма
	|	И РезультатыПроверкиУчета.ИгнорироватьПроблему");
	
	Запрос.УстановитьПараметр("КонтрольнаяСумма", КонтрольнаяСумма);
	Возврат Не Запрос.Выполнить().Пустой();
	
КонецФункции

#КонецОбласти

#КонецОбласти

#КонецОбласти